c38887b6941b1a702c64b82334a1206d2353d50f
[oweals/minetest.git] / src / mapblock.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "mapblock.h"
21 #include "map.h"
22 // For g_settings and g_irrlicht
23 #include "main.h"
24 #include "light.h"
25 #include <sstream>
26
27
28 /*
29         MapBlock
30 */
31
32 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
33                 m_parent(parent),
34                 m_pos(pos),
35                 changed(true),
36                 is_underground(false),
37                 m_lighting_expired(true),
38                 m_day_night_differs(false),
39                 //m_not_fully_generated(false),
40                 m_objects(this)
41 {
42         data = NULL;
43         if(dummy == false)
44                 reallocate();
45         
46         m_spawn_timer = -10000;
47
48 #ifndef SERVER
49         m_mesh_expired = false;
50         mesh_mutex.Init();
51         mesh = NULL;
52         m_temp_mods_mutex.Init();
53 #endif
54 }
55
56 MapBlock::~MapBlock()
57 {
58 #ifndef SERVER
59         {
60                 JMutexAutoLock lock(mesh_mutex);
61                 
62                 if(mesh)
63                 {
64                         mesh->drop();
65                         mesh = NULL;
66                 }
67         }
68 #endif
69
70         if(data)
71                 delete[] data;
72 }
73
74 bool MapBlock::isValidPositionParent(v3s16 p)
75 {
76         if(isValidPosition(p))
77         {
78                 return true;
79         }
80         else{
81                 return m_parent->isValidPosition(getPosRelative() + p);
82         }
83 }
84
85 MapNode MapBlock::getNodeParent(v3s16 p)
86 {
87         if(isValidPosition(p) == false)
88         {
89                 return m_parent->getNode(getPosRelative() + p);
90         }
91         else
92         {
93                 if(data == NULL)
94                         throw InvalidPositionException();
95                 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
96         }
97 }
98
99 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
100 {
101         if(isValidPosition(p) == false)
102         {
103                 m_parent->setNode(getPosRelative() + p, n);
104         }
105         else
106         {
107                 if(data == NULL)
108                         throw InvalidPositionException();
109                 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
110         }
111 }
112
113 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
114 {
115         if(isValidPosition(p) == false)
116         {
117                 try{
118                         return m_parent->getNode(getPosRelative() + p);
119                 }
120                 catch(InvalidPositionException &e)
121                 {
122                         return MapNode(CONTENT_IGNORE);
123                 }
124         }
125         else
126         {
127                 if(data == NULL)
128                 {
129                         return MapNode(CONTENT_IGNORE);
130                 }
131                 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
132         }
133 }
134
135 /*
136         Parameters must consist of air and !air.
137         Order doesn't matter.
138
139         If either of the nodes doesn't exist, light is 0.
140         
141         parameters:
142                 daynight_ratio: 0...1000
143                 n: getNodeParent(p)
144                 n2: getNodeParent(p + face_dir)
145                 face_dir: axis oriented unit vector from p to p2
146         
147         returns encoded light value.
148 */
149 u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
150                 v3s16 face_dir)
151 {
152         try{
153                 u8 light;
154                 u8 l1 = n.getLightBlend(daynight_ratio);
155                 u8 l2 = n2.getLightBlend(daynight_ratio);
156                 if(l1 > l2)
157                         light = l1;
158                 else
159                         light = l2;
160
161                 // Make some nice difference to different sides
162
163                 // This makes light come from a corner
164                 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
165                         light = diminish_light(diminish_light(light));
166                 else if(face_dir.X == -1 || face_dir.Z == -1)
167                         light = diminish_light(light);*/
168                 
169                 // All neighboring faces have different shade (like in minecraft)
170                 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
171                         light = diminish_light(diminish_light(light));
172                 else if(face_dir.Z == 1 || face_dir.Z == -1)
173                         light = diminish_light(light);
174
175                 return light;
176         }
177         catch(InvalidPositionException &e)
178         {
179                 return 0;
180         }
181 }
182
183 #ifndef SERVER
184
185 void makeFastFace(TileSpec tile, u8 light, v3f p,
186                 v3s16 dir, v3f scale, v3f posRelative_f,
187                 core::array<FastFace> &dest)
188 {
189         FastFace face;
190         
191         // Position is at the center of the cube.
192         v3f pos = p * BS;
193         posRelative_f *= BS;
194
195         v3f vertex_pos[4];
196         // If looking towards z+, this is the face that is behind
197         // the center point, facing towards z+.
198         vertex_pos[0] = v3f(-BS/2,-BS/2,BS/2);
199         vertex_pos[1] = v3f( BS/2,-BS/2,BS/2);
200         vertex_pos[2] = v3f( BS/2, BS/2,BS/2);
201         vertex_pos[3] = v3f(-BS/2, BS/2,BS/2);
202         
203         if(dir == v3s16(0,0,1))
204         {
205                 for(u16 i=0; i<4; i++)
206                         vertex_pos[i].rotateXZBy(0);
207         }
208         else if(dir == v3s16(0,0,-1))
209         {
210                 for(u16 i=0; i<4; i++)
211                         vertex_pos[i].rotateXZBy(180);
212         }
213         else if(dir == v3s16(1,0,0))
214         {
215                 for(u16 i=0; i<4; i++)
216                         vertex_pos[i].rotateXZBy(-90);
217         }
218         else if(dir == v3s16(-1,0,0))
219         {
220                 for(u16 i=0; i<4; i++)
221                         vertex_pos[i].rotateXZBy(90);
222         }
223         else if(dir == v3s16(0,1,0))
224         {
225                 for(u16 i=0; i<4; i++)
226                         vertex_pos[i].rotateYZBy(-90);
227         }
228         else if(dir == v3s16(0,-1,0))
229         {
230                 for(u16 i=0; i<4; i++)
231                         vertex_pos[i].rotateYZBy(90);
232         }
233
234         for(u16 i=0; i<4; i++)
235         {
236                 vertex_pos[i].X *= scale.X;
237                 vertex_pos[i].Y *= scale.Y;
238                 vertex_pos[i].Z *= scale.Z;
239                 vertex_pos[i] += pos + posRelative_f;
240         }
241
242         f32 abs_scale = 1.;
243         if     (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
244         else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
245         else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
246
247         v3f zerovector = v3f(0,0,0);
248         
249         //u8 li = decode_light(light);
250         u8 li = light;
251         //u8 li = 255; //DEBUG
252
253         u8 alpha = tile.alpha;
254         /*u8 alpha = 255;
255         if(tile.id == TILE_WATER)
256                 alpha = WATER_ALPHA;*/
257
258         video::SColor c = video::SColor(alpha,li,li,li);
259
260         float x0 = tile.texture.pos.X;
261         float y0 = tile.texture.pos.Y;
262         float w = tile.texture.size.X;
263         float h = tile.texture.size.Y;
264
265         face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
266                         core::vector2d<f32>(x0+w*abs_scale, y0+h));
267         face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
268                         core::vector2d<f32>(x0, y0+h));
269         face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
270                         core::vector2d<f32>(x0, y0));
271         face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
272                         core::vector2d<f32>(x0+w*abs_scale, y0));
273
274         face.tile = tile;
275         //DEBUG
276         //f->tile = TILE_STONE;
277         
278         dest.push_back(face);
279         //return f;
280 }
281         
282 /*
283         Gets node tile from any place relative to block.
284         Returns TILE_NODE if doesn't exist or should not be drawn.
285 */
286 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
287                 NodeModMap &temp_mods)
288 {
289         TileSpec spec;
290         spec = mn.getTile(face_dir);
291         
292         /*
293                 Check temporary modifications on this node
294         */
295         /*core::map<v3s16, NodeMod>::Node *n;
296         n = m_temp_mods.find(p);
297         // If modified
298         if(n != NULL)
299         {
300                 struct NodeMod mod = n->getValue();*/
301         NodeMod mod;
302         if(temp_mods.get(p, &mod))
303         {
304                 if(mod.type == NODEMOD_CHANGECONTENT)
305                 {
306                         MapNode mn2(mod.param);
307                         spec = mn2.getTile(face_dir);
308                 }
309                 if(mod.type == NODEMOD_CRACK)
310                 {
311                         /*
312                                 Get texture id, translate it to name, append stuff to
313                                 name, get texture id
314                         */
315
316                         // Get original texture name
317                         u32 orig_id = spec.texture.id;
318                         std::string orig_name = g_texturesource->getTextureName(orig_id);
319
320                         // Create new texture name
321                         std::ostringstream os;
322                         os<<orig_name<<"^[crack"<<mod.param;
323
324                         // Get new texture
325                         u32 new_id = g_texturesource->getTextureId(os.str());
326                         
327                         /*dstream<<"MapBlock::getNodeTile(): Switching from "
328                                         <<orig_name<<" to "<<os.str()<<" ("
329                                         <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
330                         
331                         spec.texture = g_texturesource->getTexture(new_id);
332                 }
333         }
334         
335         return spec;
336 }
337
338 u8 getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
339 {
340         /*
341                 Check temporary modifications on this node
342         */
343         /*core::map<v3s16, NodeMod>::Node *n;
344         n = m_temp_mods.find(p);
345         // If modified
346         if(n != NULL)
347         {
348                 struct NodeMod mod = n->getValue();*/
349         NodeMod mod;
350         if(temp_mods.get(p, &mod))
351         {
352                 if(mod.type == NODEMOD_CHANGECONTENT)
353                 {
354                         // Overrides content
355                         return mod.param;
356                 }
357                 if(mod.type == NODEMOD_CRACK)
358                 {
359                         /*
360                                 Content doesn't change.
361                                 
362                                 face_contents works just like it should, because
363                                 there should not be faces between differently cracked
364                                 nodes.
365
366                                 If a semi-transparent node is cracked in front an
367                                 another one, it really doesn't matter whether there
368                                 is a cracked face drawn in between or not.
369                         */
370                 }
371         }
372
373         return mn.d;
374 }
375
376 /*
377         startpos:
378         translate_dir: unit vector with only one of x, y or z
379         face_dir: unit vector with only one of x, y or z
380 */
381 void updateFastFaceRow(
382                 u32 daynight_ratio,
383                 v3f posRelative_f,
384                 v3s16 startpos,
385                 u16 length,
386                 v3s16 translate_dir,
387                 v3f translate_dir_f,
388                 v3s16 face_dir,
389                 v3f face_dir_f,
390                 core::array<FastFace> &dest,
391                 NodeModMap &temp_mods,
392                 VoxelManipulator &vmanip,
393                 v3s16 blockpos_nodes)
394 {
395         v3s16 p = startpos;
396         
397         u16 continuous_tiles_count = 0;
398         
399         MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
400         MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
401         TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
402         TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
403         u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
404
405         for(u16 j=0; j<length; j++)
406         {
407                 bool next_is_different = true;
408                 
409                 v3s16 p_next;
410                 MapNode n0_next;
411                 MapNode n1_next;
412                 TileSpec tile0_next;
413                 TileSpec tile1_next;
414                 u8 light_next = 0;
415                 
416                 // If at last position, there is nothing to compare to and
417                 // the face must be drawn anyway
418                 if(j != length - 1)
419                 {
420                         p_next = p + translate_dir;
421                         
422                         n0_next = vmanip.getNodeNoEx(blockpos_nodes + p_next);
423                         n1_next = vmanip.getNodeNoEx(blockpos_nodes + p_next + face_dir);
424                         tile0_next = getNodeTile(n0_next, p_next, face_dir, temp_mods);
425                         tile1_next = getNodeTile(n1_next,p_next+face_dir,-face_dir, temp_mods);
426                         light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
427
428                         if(tile0_next == tile0
429                                         && tile1_next == tile1
430                                         && light_next == light)
431                         {
432                                 next_is_different = false;
433                         }
434                 }
435
436                 continuous_tiles_count++;
437                 
438                 // This is set to true if the texture doesn't allow more tiling
439                 bool end_of_texture = false;
440                 /*
441                         If there is no texture, it can be tiled infinitely.
442                         If tiled==0, it means the texture can be tiled infinitely.
443                         Otherwise check tiled agains continuous_tiles_count.
444
445                         This check has to be made for both tiles, because this is
446                         a bit hackish and we know which one we're using only when
447                         the decision to make the faces is made.
448                 */
449                 if(tile0.texture.atlas != NULL && tile0.texture.tiled != 0)
450                 {
451                         if(tile0.texture.tiled <= continuous_tiles_count)
452                                 end_of_texture = true;
453                 }
454                 if(tile1.texture.atlas != NULL && tile1.texture.tiled != 0)
455                 {
456                         if(tile1.texture.tiled <= continuous_tiles_count)
457                                 end_of_texture = true;
458                 }
459                 
460                 //end_of_texture = true; //DEBUG
461                 
462                 if(next_is_different || end_of_texture)
463                 {
464                         /*
465                                 Create a face if there should be one
466                         */
467                         //u8 mf = face_contents(tile0, tile1);
468                         // This is hackish
469                         u8 content0 = getNodeContent(p, n0, temp_mods);
470                         u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
471                         u8 mf = face_contents(content0, content1);
472                         
473                         if(mf != 0)
474                         {
475                                 // Floating point conversion of the position vector
476                                 v3f pf(p.X, p.Y, p.Z);
477                                 // Center point of face (kind of)
478                                 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
479                                 v3f scale(1,1,1);
480                                 if(translate_dir.X != 0){
481                                         scale.X = continuous_tiles_count;
482                                 }
483                                 if(translate_dir.Y != 0){
484                                         scale.Y = continuous_tiles_count;
485                                 }
486                                 if(translate_dir.Z != 0){
487                                         scale.Z = continuous_tiles_count;
488                                 }
489                                 
490                                 //FastFace *f;
491
492                                 // If node at sp (tile0) is more solid
493                                 if(mf == 1)
494                                 {
495                                         makeFastFace(tile0, decode_light(light),
496                                                         sp, face_dir, scale,
497                                                         posRelative_f, dest);
498                                 }
499                                 // If node at sp is less solid (mf == 2)
500                                 else
501                                 {
502                                         makeFastFace(tile1, decode_light(light),
503                                                         sp+face_dir_f, -face_dir, scale,
504                                                         posRelative_f, dest);
505                                 }
506                                 //dest.push_back(f);
507                         }
508
509                         continuous_tiles_count = 0;
510                         n0 = n0_next;
511                         n1 = n1_next;
512                         tile0 = tile0_next;
513                         tile1 = tile1_next;
514                         light = light_next;
515                 }
516                 
517                 p = p_next;
518         }
519 }
520
521 /*
522         This is used because CMeshBuffer::append() is very slow
523 */
524 struct PreMeshBuffer
525 {
526         video::SMaterial material;
527         core::array<u16> indices;
528         core::array<video::S3DVertex> vertices;
529 };
530
531 class MeshCollector
532 {
533 public:
534         void append(
535                         video::SMaterial material,
536                         const video::S3DVertex* const vertices,
537                         u32 numVertices,
538                         const u16* const indices,
539                         u32 numIndices
540                 )
541         {
542                 PreMeshBuffer *p = NULL;
543                 for(u32 i=0; i<m_prebuffers.size(); i++)
544                 {
545                         PreMeshBuffer &pp = m_prebuffers[i];
546                         if(pp.material != material)
547                                 continue;
548
549                         p = &pp;
550                         break;
551                 }
552
553                 if(p == NULL)
554                 {
555                         PreMeshBuffer pp;
556                         pp.material = material;
557                         m_prebuffers.push_back(pp);
558                         p = &m_prebuffers[m_prebuffers.size()-1];
559                 }
560
561                 u32 vertex_count = p->vertices.size();
562                 for(u32 i=0; i<numIndices; i++)
563                 {
564                         u32 j = indices[i] + vertex_count;
565                         if(j > 65535)
566                         {
567                                 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
568                                 // NOTE: Fix is to just add an another MeshBuffer
569                         }
570                         p->indices.push_back(j);
571                 }
572                 for(u32 i=0; i<numVertices; i++)
573                 {
574                         p->vertices.push_back(vertices[i]);
575                 }
576         }
577
578         void fillMesh(scene::SMesh *mesh)
579         {
580                 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
581                                 <<" meshbuffers"<<std::endl;*/
582                 for(u32 i=0; i<m_prebuffers.size(); i++)
583                 {
584                         PreMeshBuffer &p = m_prebuffers[i];
585
586                         /*dstream<<"p.vertices.size()="<<p.vertices.size()
587                                         <<", p.indices.size()="<<p.indices.size()
588                                         <<std::endl;*/
589                         
590                         // Create meshbuffer
591                         
592                         // This is a "Standard MeshBuffer",
593                         // it's a typedeffed CMeshBuffer<video::S3DVertex>
594                         scene::SMeshBuffer *buf = new scene::SMeshBuffer();
595                         // Set material
596                         buf->Material = p.material;
597                         //((scene::SMeshBuffer*)buf)->Material = p.material;
598                         // Use VBO
599                         //buf->setHardwareMappingHint(scene::EHM_STATIC);
600                         // Add to mesh
601                         mesh->addMeshBuffer(buf);
602                         // Mesh grabbed it
603                         buf->drop();
604
605                         buf->append(p.vertices.pointer(), p.vertices.size(),
606                                         p.indices.pointer(), p.indices.size());
607                 }
608         }
609
610 private:
611         core::array<PreMeshBuffer> m_prebuffers;
612 };
613
614 scene::SMesh* makeMapBlockMesh(
615                 u32 daynight_ratio,
616                 NodeModMap &temp_mods,
617                 VoxelManipulator &vmanip,
618                 v3s16 blockpos_nodes)
619 {
620         // 4-21ms for MAP_BLOCKSIZE=16
621         // 24-155ms for MAP_BLOCKSIZE=32
622         //TimeTaker timer1("makeMapBlockMesh()");
623
624         core::array<FastFace> fastfaces_new;
625         
626         // floating point conversion
627         v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
628         
629         /*
630                 Some settings
631         */
632         bool new_style_water = g_settings.getBool("new_style_water");
633         bool new_style_leaves = g_settings.getBool("new_style_leaves");
634         
635         float node_water_level = 1.0;
636         if(new_style_water)
637                 node_water_level = 0.85;
638         
639         /*
640                 We are including the faces of the trailing edges of the block.
641                 This means that when something changes, the caller must
642                 also update the meshes of the blocks at the leading edges.
643
644                 NOTE: This is the slowest part of this method.
645         */
646         
647         {
648                 // 4-23ms for MAP_BLOCKSIZE=16
649                 //TimeTaker timer2("updateMesh() collect");
650
651                 /*
652                         Go through every y,z and get top faces in rows of x+
653                 */
654                 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
655                         for(s16 z=0; z<MAP_BLOCKSIZE; z++){
656                                 updateFastFaceRow(daynight_ratio, posRelative_f,
657                                                 v3s16(0,y,z), MAP_BLOCKSIZE,
658                                                 v3s16(1,0,0), //dir
659                                                 v3f  (1,0,0),
660                                                 v3s16(0,1,0), //face dir
661                                                 v3f  (0,1,0),
662                                                 fastfaces_new,
663                                                 temp_mods,
664                                                 vmanip,
665                                                 blockpos_nodes);
666                         }
667                 }
668                 /*
669                         Go through every x,y and get right faces in rows of z+
670                 */
671                 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
672                         for(s16 y=0; y<MAP_BLOCKSIZE; y++){
673                                 updateFastFaceRow(daynight_ratio, posRelative_f,
674                                                 v3s16(x,y,0), MAP_BLOCKSIZE,
675                                                 v3s16(0,0,1),
676                                                 v3f  (0,0,1),
677                                                 v3s16(1,0,0),
678                                                 v3f  (1,0,0),
679                                                 fastfaces_new,
680                                                 temp_mods,
681                                                 vmanip,
682                                                 blockpos_nodes);
683                         }
684                 }
685                 /*
686                         Go through every y,z and get back faces in rows of x+
687                 */
688                 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
689                         for(s16 y=0; y<MAP_BLOCKSIZE; y++){
690                                 updateFastFaceRow(daynight_ratio, posRelative_f,
691                                                 v3s16(0,y,z), MAP_BLOCKSIZE,
692                                                 v3s16(1,0,0),
693                                                 v3f  (1,0,0),
694                                                 v3s16(0,0,1),
695                                                 v3f  (0,0,1),
696                                                 fastfaces_new,
697                                                 temp_mods,
698                                                 vmanip,
699                                                 blockpos_nodes);
700                         }
701                 }
702         }
703
704         // End of slow part
705
706         /*
707                 Convert FastFaces to SMesh
708         */
709
710         MeshCollector collector;
711
712         if(fastfaces_new.size() > 0)
713         {
714                 // avg 0ms (100ms spikes when loading textures the first time)
715                 //TimeTaker timer2("updateMesh() mesh building");
716
717                 video::SMaterial material;
718                 material.setFlag(video::EMF_LIGHTING, false);
719                 material.setFlag(video::EMF_BILINEAR_FILTER, false);
720                 material.setFlag(video::EMF_FOG_ENABLE, true);
721                 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
722                 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
723
724                 for(u32 i=0; i<fastfaces_new.size(); i++)
725                 {
726                         FastFace &f = fastfaces_new[i];
727
728                         const u16 indices[] = {0,1,2,2,3,0};
729                         
730                         //video::ITexture *texture = g_irrlicht->getTexture(f.tile.spec);
731                         video::ITexture *texture = f.tile.texture.atlas;
732                         if(texture == NULL)
733                                 continue;
734
735                         material.setTexture(0, texture);
736                         
737                         f.tile.applyMaterialOptions(material);
738                         
739                         collector.append(material, f.vertices, 4, indices, 6);
740                 }
741         }
742
743         /*
744                 Add special graphics:
745                 - torches
746                 - flowing water
747         */
748
749         // 0ms
750         //TimeTaker timer2("updateMesh() adding special stuff");
751
752         // Flowing water material
753         video::SMaterial material_water1;
754         material_water1.setFlag(video::EMF_LIGHTING, false);
755         //material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
756         material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
757         material_water1.setFlag(video::EMF_FOG_ENABLE, true);
758         material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
759         //TODO
760         //material_water1.setTexture(0, g_irrlicht->getTexture("water.png"));
761         AtlasPointer pa_water1 = g_texturesource->getTexture(
762                         g_texturesource->getTextureId("water.png"));
763         material_water1.setTexture(0, pa_water1.atlas);
764
765         // New-style leaves material
766         video::SMaterial material_leaves1;
767         material_leaves1.setFlag(video::EMF_LIGHTING, false);
768         //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
769         material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
770         material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
771         material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
772         //TODO
773         //material_leaves1.setTexture(0, g_irrlicht->getTexture("leaves.png"));
774         AtlasPointer pa_leaves1 = g_texturesource->getTexture(
775                         g_texturesource->getTextureId("leaves.png"));
776         material_leaves1.setTexture(0, pa_leaves1.atlas);
777
778         for(s16 z=0; z<MAP_BLOCKSIZE; z++)
779         for(s16 y=0; y<MAP_BLOCKSIZE; y++)
780         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
781         {
782                 v3s16 p(x,y,z);
783
784                 MapNode &n = vmanip.getNodeRef(blockpos_nodes+p);
785                 
786                 /*
787                         Add torches to mesh
788                 */
789                 if(n.d == CONTENT_TORCH)
790                 {
791                         video::SColor c(255,255,255,255);
792
793                         // Wall at X+ of node
794                         video::S3DVertex vertices[4] =
795                         {
796                                 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
797                                 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
798                                 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
799                                 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
800                         };
801
802                         v3s16 dir = unpackDir(n.dir);
803
804                         for(s32 i=0; i<4; i++)
805                         {
806                                 if(dir == v3s16(1,0,0))
807                                         vertices[i].Pos.rotateXZBy(0);
808                                 if(dir == v3s16(-1,0,0))
809                                         vertices[i].Pos.rotateXZBy(180);
810                                 if(dir == v3s16(0,0,1))
811                                         vertices[i].Pos.rotateXZBy(90);
812                                 if(dir == v3s16(0,0,-1))
813                                         vertices[i].Pos.rotateXZBy(-90);
814                                 if(dir == v3s16(0,-1,0))
815                                         vertices[i].Pos.rotateXZBy(45);
816                                 if(dir == v3s16(0,1,0))
817                                         vertices[i].Pos.rotateXZBy(-45);
818
819                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
820                         }
821
822                         // Set material
823                         video::SMaterial material;
824                         material.setFlag(video::EMF_LIGHTING, false);
825                         material.setFlag(video::EMF_BACK_FACE_CULLING, false);
826                         material.setFlag(video::EMF_BILINEAR_FILTER, false);
827                         //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
828                         material.MaterialType
829                                         = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
830
831                         if(dir == v3s16(0,-1,0))
832                                 material.setTexture(0,
833                                                 g_texturesource->getTextureRaw("torch_on_floor.png"));
834                         else if(dir == v3s16(0,1,0))
835                                 material.setTexture(0,
836                                                 g_texturesource->getTextureRaw("torch_on_ceiling.png"));
837                         // For backwards compatibility
838                         else if(dir == v3s16(0,0,0))
839                                 material.setTexture(0,
840                                                 g_texturesource->getTextureRaw("torch_on_floor.png"));
841                         else
842                                 material.setTexture(0, 
843                                                 g_texturesource->getTextureRaw("torch.png"));
844
845                         u16 indices[] = {0,1,2,2,3,0};
846                         // Add to mesh collector
847                         collector.append(material, vertices, 4, indices, 6);
848                 }
849                 /*
850                         Signs on walls
851                 */
852                 if(n.d == CONTENT_SIGN_WALL)
853                 {
854                         u8 l = decode_light(n.getLightBlend(daynight_ratio));
855                         video::SColor c(255,l,l,l);
856                                 
857                         float d = (float)BS/16;
858                         // Wall at X+ of node
859                         video::S3DVertex vertices[4] =
860                         {
861                                 video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1),
862                                 video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1),
863                                 video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0),
864                                 video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
865                         };
866
867                         v3s16 dir = unpackDir(n.dir);
868
869                         for(s32 i=0; i<4; i++)
870                         {
871                                 if(dir == v3s16(1,0,0))
872                                         vertices[i].Pos.rotateXZBy(0);
873                                 if(dir == v3s16(-1,0,0))
874                                         vertices[i].Pos.rotateXZBy(180);
875                                 if(dir == v3s16(0,0,1))
876                                         vertices[i].Pos.rotateXZBy(90);
877                                 if(dir == v3s16(0,0,-1))
878                                         vertices[i].Pos.rotateXZBy(-90);
879                                 if(dir == v3s16(0,-1,0))
880                                         vertices[i].Pos.rotateXYBy(-90);
881                                 if(dir == v3s16(0,1,0))
882                                         vertices[i].Pos.rotateXYBy(90);
883
884                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
885                         }
886
887                         // Set material
888                         video::SMaterial material;
889                         material.setFlag(video::EMF_LIGHTING, false);
890                         material.setFlag(video::EMF_BACK_FACE_CULLING, false);
891                         material.setFlag(video::EMF_BILINEAR_FILTER, false);
892                         material.setFlag(video::EMF_FOG_ENABLE, true);
893                         //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
894                         material.MaterialType
895                                         = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
896
897                         material.setTexture(0, 
898                                         g_texturesource->getTextureRaw("sign_wall.png"));
899
900                         u16 indices[] = {0,1,2,2,3,0};
901                         // Add to mesh collector
902                         collector.append(material, vertices, 4, indices, 6);
903                 }
904                 /*
905                         Add flowing water to mesh
906                 */
907                 else if(n.d == CONTENT_WATER)
908                 {
909                         bool top_is_water = false;
910                         try{
911                                 MapNode n = vmanip.getNode(blockpos_nodes + v3s16(x,y+1,z));
912                                 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
913                                         top_is_water = true;
914                         }catch(InvalidPositionException &e){}
915                         
916                         u8 l = decode_light(n.getLightBlend(daynight_ratio));
917                         video::SColor c(WATER_ALPHA,l,l,l);
918                         
919                         // Neighbor water levels (key = relative position)
920                         // Includes current node
921                         core::map<v3s16, f32> neighbor_levels;
922                         core::map<v3s16, u8> neighbor_contents;
923                         core::map<v3s16, u8> neighbor_flags;
924                         const u8 neighborflag_top_is_water = 0x01;
925                         v3s16 neighbor_dirs[9] = {
926                                 v3s16(0,0,0),
927                                 v3s16(0,0,1),
928                                 v3s16(0,0,-1),
929                                 v3s16(1,0,0),
930                                 v3s16(-1,0,0),
931                                 v3s16(1,0,1),
932                                 v3s16(-1,0,-1),
933                                 v3s16(1,0,-1),
934                                 v3s16(-1,0,1),
935                         };
936                         for(u32 i=0; i<9; i++)
937                         {
938                                 u8 content = CONTENT_AIR;
939                                 float level = -0.5 * BS;
940                                 u8 flags = 0;
941                                 try{
942                                         // Check neighbor
943                                         v3s16 p2 = p + neighbor_dirs[i];
944                                         MapNode n2 = vmanip.getNode(blockpos_nodes + p2);
945
946                                         content = n2.d;
947
948                                         if(n2.d == CONTENT_WATERSOURCE)
949                                                 level = (-0.5+node_water_level) * BS;
950                                         else if(n2.d == CONTENT_WATER)
951                                                 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
952                                                                 * node_water_level) * BS;
953
954                                         // Check node above neighbor.
955                                         // NOTE: This doesn't get executed if neighbor
956                                         //       doesn't exist
957                                         p2.Y += 1;
958                                         n2 = vmanip.getNode(blockpos_nodes + p2);
959                                         if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
960                                                 flags |= neighborflag_top_is_water;
961                                 }
962                                 catch(InvalidPositionException &e){}
963                                 
964                                 neighbor_levels.insert(neighbor_dirs[i], level);
965                                 neighbor_contents.insert(neighbor_dirs[i], content);
966                                 neighbor_flags.insert(neighbor_dirs[i], flags);
967                         }
968
969                         //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
970                         //float water_level = neighbor_levels[v3s16(0,0,0)];
971
972                         // Corner heights (average between four waters)
973                         f32 corner_levels[4];
974                         
975                         v3s16 halfdirs[4] = {
976                                 v3s16(0,0,0),
977                                 v3s16(1,0,0),
978                                 v3s16(1,0,1),
979                                 v3s16(0,0,1),
980                         };
981                         for(u32 i=0; i<4; i++)
982                         {
983                                 v3s16 cornerdir = halfdirs[i];
984                                 float cornerlevel = 0;
985                                 u32 valid_count = 0;
986                                 for(u32 j=0; j<4; j++)
987                                 {
988                                         v3s16 neighbordir = cornerdir - halfdirs[j];
989                                         u8 content = neighbor_contents[neighbordir];
990                                         // Special case for source nodes
991                                         if(content == CONTENT_WATERSOURCE)
992                                         {
993                                                 cornerlevel = (-0.5+node_water_level)*BS;
994                                                 valid_count = 1;
995                                                 break;
996                                         }
997                                         else if(content == CONTENT_WATER)
998                                         {
999                                                 cornerlevel += neighbor_levels[neighbordir];
1000                                                 valid_count++;
1001                                         }
1002                                         else if(content == CONTENT_AIR)
1003                                         {
1004                                                 cornerlevel += -0.5*BS;
1005                                                 valid_count++;
1006                                         }
1007                                 }
1008                                 if(valid_count > 0)
1009                                         cornerlevel /= valid_count;
1010                                 corner_levels[i] = cornerlevel;
1011                         }
1012
1013                         /*
1014                                 Generate sides
1015                         */
1016
1017                         v3s16 side_dirs[4] = {
1018                                 v3s16(1,0,0),
1019                                 v3s16(-1,0,0),
1020                                 v3s16(0,0,1),
1021                                 v3s16(0,0,-1),
1022                         };
1023                         s16 side_corners[4][2] = {
1024                                 {1, 2},
1025                                 {3, 0},
1026                                 {2, 3},
1027                                 {0, 1},
1028                         };
1029                         for(u32 i=0; i<4; i++)
1030                         {
1031                                 v3s16 dir = side_dirs[i];
1032
1033                                 /*
1034                                         If our topside is water and neighbor's topside
1035                                         is water, don't draw side face
1036                                 */
1037                                 if(top_is_water &&
1038                                                 neighbor_flags[dir] & neighborflag_top_is_water)
1039                                         continue;
1040
1041                                 u8 neighbor_content = neighbor_contents[dir];
1042                                 
1043                                 // Don't draw face if neighbor is not air or water
1044                                 if(neighbor_content != CONTENT_AIR
1045                                                 && neighbor_content != CONTENT_WATER)
1046                                         continue;
1047                                 
1048                                 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
1049                                 
1050                                 // Don't draw any faces if neighbor is water and top is water
1051                                 if(neighbor_is_water == true && top_is_water == false)
1052                                         continue;
1053                                 
1054                                 video::S3DVertex vertices[4] =
1055                                 {
1056                                         /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1057                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1058                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1059                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1060                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1061                                                         pa_water1.x0(), pa_water1.y1()),
1062                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1063                                                         pa_water1.x1(), pa_water1.y1()),
1064                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1065                                                         pa_water1.x1(), pa_water1.y0()),
1066                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1067                                                         pa_water1.x0(), pa_water1.y0()),
1068                                 };
1069                                 
1070                                 /*
1071                                         If our topside is water, set upper border of face
1072                                         at upper border of node
1073                                 */
1074                                 if(top_is_water)
1075                                 {
1076                                         vertices[2].Pos.Y = 0.5*BS;
1077                                         vertices[3].Pos.Y = 0.5*BS;
1078                                 }
1079                                 /*
1080                                         Otherwise upper position of face is corner levels
1081                                 */
1082                                 else
1083                                 {
1084                                         vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1085                                         vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1086                                 }
1087                                 
1088                                 /*
1089                                         If neighbor is water, lower border of face is corner
1090                                         water levels
1091                                 */
1092                                 if(neighbor_is_water)
1093                                 {
1094                                         vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1095                                         vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1096                                 }
1097                                 /*
1098                                         If neighbor is not water, lower border of face is
1099                                         lower border of node
1100                                 */
1101                                 else
1102                                 {
1103                                         vertices[0].Pos.Y = -0.5*BS;
1104                                         vertices[1].Pos.Y = -0.5*BS;
1105                                 }
1106                                 
1107                                 for(s32 j=0; j<4; j++)
1108                                 {
1109                                         if(dir == v3s16(0,0,1))
1110                                                 vertices[j].Pos.rotateXZBy(0);
1111                                         if(dir == v3s16(0,0,-1))
1112                                                 vertices[j].Pos.rotateXZBy(180);
1113                                         if(dir == v3s16(-1,0,0))
1114                                                 vertices[j].Pos.rotateXZBy(90);
1115                                         if(dir == v3s16(1,0,-0))
1116                                                 vertices[j].Pos.rotateXZBy(-90);
1117
1118                                         vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
1119                                 }
1120
1121                                 u16 indices[] = {0,1,2,2,3,0};
1122                                 // Add to mesh collector
1123                                 collector.append(material_water1, vertices, 4, indices, 6);
1124                         }
1125                         
1126                         /*
1127                                 Generate top side, if appropriate
1128                         */
1129                         
1130                         if(top_is_water == false)
1131                         {
1132                                 video::S3DVertex vertices[4] =
1133                                 {
1134                                         /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1135                                         video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1136                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1137                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1138                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1139                                                         pa_water1.x0(), pa_water1.y1()),
1140                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1141                                                         pa_water1.x1(), pa_water1.y1()),
1142                                         video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1143                                                         pa_water1.x1(), pa_water1.y0()),
1144                                         video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1145                                                         pa_water1.x0(), pa_water1.y0()),
1146                                 };
1147                                 
1148                                 // This fixes a strange bug
1149                                 s32 corner_resolve[4] = {3,2,1,0};
1150
1151                                 for(s32 i=0; i<4; i++)
1152                                 {
1153                                         //vertices[i].Pos.Y += water_level;
1154                                         //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1155                                         s32 j = corner_resolve[i];
1156                                         vertices[i].Pos.Y += corner_levels[j];
1157                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1158                                 }
1159
1160                                 u16 indices[] = {0,1,2,2,3,0};
1161                                 // Add to mesh collector
1162                                 collector.append(material_water1, vertices, 4, indices, 6);
1163                         }
1164                 }
1165                 /*
1166                         Add water sources to mesh if using new style
1167                 */
1168                 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1169                 {
1170                         //bool top_is_water = false;
1171                         bool top_is_air = false;
1172                         try{
1173                                 MapNode n = vmanip.getNode(blockpos_nodes + v3s16(x,y+1,z));
1174                                 /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1175                                         top_is_water = true;*/
1176                                 if(n.d == CONTENT_AIR)
1177                                         top_is_air = true;
1178                         }catch(InvalidPositionException &e){}
1179                         
1180                         /*if(top_is_water == true)
1181                                 continue;*/
1182                         if(top_is_air == false)
1183                                 continue;
1184
1185                         u8 l = decode_light(n.getLightBlend(daynight_ratio));
1186                         video::SColor c(WATER_ALPHA,l,l,l);
1187                         
1188                         video::S3DVertex vertices[4] =
1189                         {
1190                                 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1191                                 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1192                                 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1193                                 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1194                                 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1195                                                 pa_water1.x0(), pa_water1.y1()),
1196                                 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1197                                                 pa_water1.x1(), pa_water1.y1()),
1198                                 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1199                                                 pa_water1.x1(), pa_water1.y0()),
1200                                 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1201                                                 pa_water1.x0(), pa_water1.y0()),
1202                         };
1203
1204                         for(s32 i=0; i<4; i++)
1205                         {
1206                                 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1207                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1208                         }
1209
1210                         u16 indices[] = {0,1,2,2,3,0};
1211                         // Add to mesh collector
1212                         collector.append(material_water1, vertices, 4, indices, 6);
1213                 }
1214                 /*
1215                         Add leaves if using new style
1216                 */
1217                 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1218                 {
1219                         /*u8 l = decode_light(n.getLightBlend(daynight_ratio));*/
1220                         u8 l = decode_light(undiminish_light(n.getLightBlend(daynight_ratio)));
1221                         video::SColor c(255,l,l,l);
1222
1223                         for(u32 j=0; j<6; j++)
1224                         {
1225                                 video::S3DVertex vertices[4] =
1226                                 {
1227                                         /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1228                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1229                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1230                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1231                                         video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1232                                                 pa_leaves1.x0(), pa_leaves1.y1()),
1233                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1234                                                 pa_leaves1.x1(), pa_leaves1.y1()),
1235                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1236                                                 pa_leaves1.x1(), pa_leaves1.y0()),
1237                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1238                                                 pa_leaves1.x0(), pa_leaves1.y0()),
1239                                 };
1240
1241                                 if(j == 0)
1242                                 {
1243                                         for(u16 i=0; i<4; i++)
1244                                                 vertices[i].Pos.rotateXZBy(0);
1245                                 }
1246                                 else if(j == 1)
1247                                 {
1248                                         for(u16 i=0; i<4; i++)
1249                                                 vertices[i].Pos.rotateXZBy(180);
1250                                 }
1251                                 else if(j == 2)
1252                                 {
1253                                         for(u16 i=0; i<4; i++)
1254                                                 vertices[i].Pos.rotateXZBy(-90);
1255                                 }
1256                                 else if(j == 3)
1257                                 {
1258                                         for(u16 i=0; i<4; i++)
1259                                                 vertices[i].Pos.rotateXZBy(90);
1260                                 }
1261                                 else if(j == 4)
1262                                 {
1263                                         for(u16 i=0; i<4; i++)
1264                                                 vertices[i].Pos.rotateYZBy(-90);
1265                                 }
1266                                 else if(j == 5)
1267                                 {
1268                                         for(u16 i=0; i<4; i++)
1269                                                 vertices[i].Pos.rotateYZBy(90);
1270                                 }
1271
1272                                 for(u16 i=0; i<4; i++)
1273                                 {
1274                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1275                                 }
1276
1277                                 u16 indices[] = {0,1,2,2,3,0};
1278                                 // Add to mesh collector
1279                                 collector.append(material_leaves1, vertices, 4, indices, 6);
1280                         }
1281                 }
1282         }
1283
1284         /*
1285                 Add stuff from collector to mesh
1286         */
1287         
1288         scene::SMesh *mesh_new = NULL;
1289         mesh_new = new scene::SMesh();
1290         
1291         collector.fillMesh(mesh_new);
1292
1293         /*
1294                 Do some stuff to the mesh
1295         */
1296
1297         mesh_new->recalculateBoundingBox();
1298
1299         /*
1300                 Delete new mesh if it is empty
1301         */
1302
1303         if(mesh_new->getMeshBufferCount() == 0)
1304         {
1305                 mesh_new->drop();
1306                 mesh_new = NULL;
1307         }
1308
1309         if(mesh_new)
1310         {
1311 #if 0
1312                 // Usually 1-700 faces and 1-7 materials
1313                 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1314                                 <<"and uses "<<mesh_new->getMeshBufferCount()
1315                                 <<" materials (meshbuffers)"<<std::endl;
1316 #endif
1317
1318                 // Use VBO for mesh (this just would set this for ever buffer)
1319                 // This will lead to infinite memory usage because or irrlicht.
1320                 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1321
1322                 /*
1323                         NOTE: If that is enabled, some kind of a queue to the main
1324                         thread should be made which would call irrlicht to delete
1325                         the hardware buffer and then delete the mesh
1326                 */
1327         }
1328
1329         return mesh_new;
1330         
1331         //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1332 }
1333
1334 /*scene::SMesh* makeMapBlockMesh(
1335                 u32 daynight_ratio,
1336                 NodeModMap &temp_mods,
1337                 VoxelManipulator &vmanip,
1338                 v3s16 blockpos_nodes)
1339 {
1340 }*/
1341
1342 #if 1
1343 void MapBlock::updateMesh(u32 daynight_ratio)
1344 {
1345 #if 0
1346         /*
1347                 DEBUG: If mesh has been generated, don't generate it again
1348         */
1349         {
1350                 JMutexAutoLock meshlock(mesh_mutex);
1351                 if(mesh != NULL)
1352                         return;
1353         }
1354 #endif
1355
1356         /*
1357                 Avoid interlocks by copying m_temp_mods
1358         */
1359         NodeModMap temp_mods;
1360         copyTempMods(temp_mods);
1361         
1362         v3s16 blockpos_nodes = getPosRelative();
1363
1364         VoxelManipulator vmanip;
1365         // Allocate this block + borders
1366         vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1),
1367                         blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE));
1368         // Copy our data
1369         copyTo(vmanip);
1370         // Copy borders from map
1371         // +-Z
1372         for(s16 x=-1; x<=MAP_BLOCKSIZE; x++)
1373         for(s16 y=-1; y<=MAP_BLOCKSIZE; y++)
1374         for(s16 z=-1; z<=MAP_BLOCKSIZE; z+=MAP_BLOCKSIZE+1)
1375         {
1376                 v3s16 p(x,y,z);
1377                 vmanip.setNodeNoRef(blockpos_nodes+p, getNodeParentNoEx(p));
1378         }
1379         // +-Y
1380         for(s16 x=-1; x<=MAP_BLOCKSIZE; x++)
1381         for(s16 y=-1; y<=MAP_BLOCKSIZE; y+=MAP_BLOCKSIZE+1)
1382         for(s16 z=-1; z<=MAP_BLOCKSIZE; z++)
1383         {
1384                 v3s16 p(x,y,z);
1385                 vmanip.setNodeNoRef(blockpos_nodes+p, getNodeParentNoEx(p));
1386         }
1387         // +-Z
1388         for(s16 x=-1; x<=MAP_BLOCKSIZE; x+=MAP_BLOCKSIZE+1)
1389         for(s16 y=-1; y<=MAP_BLOCKSIZE; y++)
1390         for(s16 z=-1; z<=MAP_BLOCKSIZE; z++)
1391         {
1392                 v3s16 p(x,y,z);
1393                 vmanip.setNodeNoRef(blockpos_nodes+p, getNodeParentNoEx(p));
1394         }
1395         
1396         scene::SMesh *mesh_new = makeMapBlockMesh(
1397                         daynight_ratio,
1398                         temp_mods,
1399                         vmanip,
1400                         getPosRelative()
1401         );
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                 /*if(m_not_fully_generated)
1882                         flags |= 0x08;*/
1883                 os.write((char*)&flags, 1);
1884
1885                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1886
1887                 /*
1888                         Get data
1889                 */
1890
1891                 SharedBuffer<u8> databuf(nodecount*3);
1892
1893                 // Get contents
1894                 for(u32 i=0; i<nodecount; i++)
1895                 {
1896                         databuf[i] = data[i].d;
1897                 }
1898
1899                 // Get params
1900                 for(u32 i=0; i<nodecount; i++)
1901                 {
1902                         databuf[i+nodecount] = data[i].param;
1903                 }
1904
1905                 // Get param2
1906                 for(u32 i=0; i<nodecount; i++)
1907                 {
1908                         databuf[i+nodecount*2] = data[i].param2;
1909                 }
1910
1911                 /*
1912                         Compress data to output stream
1913                 */
1914
1915                 compress(databuf, os, version);
1916         }
1917 }
1918
1919 void MapBlock::deSerialize(std::istream &is, u8 version)
1920 {
1921         if(!ser_ver_supported(version))
1922                 throw VersionMismatchException("ERROR: MapBlock format not supported");
1923
1924         // These have no compression
1925         if(version <= 3 || version == 5 || version == 6)
1926         {
1927                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1928                 char tmp;
1929                 is.read(&tmp, 1);
1930                 if(is.gcount() != 1)
1931                         throw SerializationError
1932                                         ("MapBlock::deSerialize: no enough input data");
1933                 is_underground = tmp;
1934                 for(u32 i=0; i<nodecount; i++)
1935                 {
1936                         s32 len = MapNode::serializedLength(version);
1937                         SharedBuffer<u8> d(len);
1938                         is.read((char*)*d, len);
1939                         if(is.gcount() != len)
1940                                 throw SerializationError
1941                                                 ("MapBlock::deSerialize: no enough input data");
1942                         data[i].deSerialize(*d, version);
1943                 }
1944         }
1945         else if(version <= 10)
1946         {
1947                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1948
1949                 u8 t8;
1950                 is.read((char*)&t8, 1);
1951                 is_underground = t8;
1952
1953                 {
1954                         // Uncompress and set material data
1955                         std::ostringstream os(std::ios_base::binary);
1956                         decompress(is, os, version);
1957                         std::string s = os.str();
1958                         if(s.size() != nodecount)
1959                                 throw SerializationError
1960                                                 ("MapBlock::deSerialize: invalid format");
1961                         for(u32 i=0; i<s.size(); i++)
1962                         {
1963                                 data[i].d = s[i];
1964                         }
1965                 }
1966                 {
1967                         // Uncompress and set param data
1968                         std::ostringstream os(std::ios_base::binary);
1969                         decompress(is, os, version);
1970                         std::string s = os.str();
1971                         if(s.size() != nodecount)
1972                                 throw SerializationError
1973                                                 ("MapBlock::deSerialize: invalid format");
1974                         for(u32 i=0; i<s.size(); i++)
1975                         {
1976                                 data[i].param = s[i];
1977                         }
1978                 }
1979         
1980                 if(version >= 10)
1981                 {
1982                         // Uncompress and set param2 data
1983                         std::ostringstream os(std::ios_base::binary);
1984                         decompress(is, os, version);
1985                         std::string s = os.str();
1986                         if(s.size() != nodecount)
1987                                 throw SerializationError
1988                                                 ("MapBlock::deSerialize: invalid format");
1989                         for(u32 i=0; i<s.size(); i++)
1990                         {
1991                                 data[i].param2 = s[i];
1992                         }
1993                 }
1994         }
1995         // All other versions (newest)
1996         else
1997         {
1998                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1999
2000                 u8 flags;
2001                 is.read((char*)&flags, 1);
2002                 is_underground = (flags & 0x01) ? true : false;
2003                 m_day_night_differs = (flags & 0x02) ? true : false;
2004                 m_lighting_expired = (flags & 0x04) ? true : false;
2005                 //m_not_fully_generated = (flags & 0x08) ? true : false;
2006
2007                 // Uncompress data
2008                 std::ostringstream os(std::ios_base::binary);
2009                 decompress(is, os, version);
2010                 std::string s = os.str();
2011                 if(s.size() != nodecount*3)
2012                         throw SerializationError
2013                                         ("MapBlock::deSerialize: invalid format");
2014
2015                 // Set contents
2016                 for(u32 i=0; i<nodecount; i++)
2017                 {
2018                         data[i].d = s[i];
2019                 }
2020                 // Set params
2021                 for(u32 i=0; i<nodecount; i++)
2022                 {
2023                         data[i].param = s[i+nodecount];
2024                 }
2025                 // Set param2
2026                 for(u32 i=0; i<nodecount; i++)
2027                 {
2028                         data[i].param2 = s[i+nodecount*2];
2029                 }
2030         }
2031         
2032         /*
2033                 Translate nodes as specified in the translate_to fields of
2034                 node features
2035         */
2036         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2037         {
2038                 MapNode &n = data[i];
2039
2040                 MapNode *translate_to = content_features(n.d).translate_to;
2041                 if(translate_to)
2042                 {
2043                         dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
2044                                         <<translate_to->d<<std::endl;
2045                         n = *translate_to;
2046                 }
2047         }
2048 }
2049
2050
2051 //END