]> git.lizzy.rs Git - minetest.git/blob - src/mapblock.cpp
71044652d902cae7a2cb9213ac4e33b788e07e7a
[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
23 #include "main.h"
24 #include "light.h"
25 #include <sstream>
26
27 #ifndef SERVER
28 void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block)
29 {
30         m_daynight_ratio = daynight_ratio;
31         m_blockpos = block->getPos();
32
33         v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
34         
35         /*
36                 There is no harm not copying the TempMods of the neighbors
37                 because they are already copied to this block
38         */
39         m_temp_mods.clear();
40         block->copyTempMods(m_temp_mods);
41         
42         /*
43                 Copy data
44         */
45
46         // Allocate this block + neighbors
47         m_vmanip.clear();
48         m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
49                         blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
50
51         {
52                 //TimeTaker timer("copy central block data");
53                 // 0ms
54
55                 // Copy our data
56                 block->copyTo(m_vmanip);
57         }
58         {
59                 //TimeTaker timer("copy neighbor block data");
60                 // 0ms
61
62                 /*
63                         Copy neighbors. This is lightning fast.
64                         Copying only the borders would be *very* slow.
65                 */
66                 
67                 // Get map
68                 NodeContainer *parentcontainer = block->getParent();
69                 // This will only work if the parent is the map
70                 assert(parentcontainer->nodeContainerId() == NODECONTAINER_ID_MAP);
71                 // OK, we have the map!
72                 Map *map = (Map*)parentcontainer;
73
74                 for(u16 i=0; i<6; i++)
75                 {
76                         const v3s16 &dir = g_6dirs[i];
77                         v3s16 bp = m_blockpos + dir;
78                         MapBlock *b = map->getBlockNoCreateNoEx(bp);
79                         if(b)
80                                 b->copyTo(m_vmanip);
81                 }
82         }
83 }
84 #endif
85
86 /*
87         Parameters must consist of air and !air.
88         Order doesn't matter.
89
90         If either of the nodes doesn't exist, light is 0.
91         
92         parameters:
93                 daynight_ratio: 0...1000
94                 n: getNodeParent(p)
95                 n2: getNodeParent(p + face_dir)
96                 face_dir: axis oriented unit vector from p to p2
97         
98         returns encoded light value.
99 */
100 u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
101                 v3s16 face_dir)
102 {
103         try{
104                 u8 light;
105                 u8 l1 = n.getLightBlend(daynight_ratio);
106                 u8 l2 = n2.getLightBlend(daynight_ratio);
107                 if(l1 > l2)
108                         light = l1;
109                 else
110                         light = l2;
111
112                 // Make some nice difference to different sides
113
114                 // This makes light come from a corner
115                 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
116                         light = diminish_light(diminish_light(light));
117                 else if(face_dir.X == -1 || face_dir.Z == -1)
118                         light = diminish_light(light);*/
119                 
120                 // All neighboring faces have different shade (like in minecraft)
121                 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
122                         light = diminish_light(diminish_light(light));
123                 else if(face_dir.Z == 1 || face_dir.Z == -1)
124                         light = diminish_light(light);
125
126                 return light;
127         }
128         catch(InvalidPositionException &e)
129         {
130                 return 0;
131         }
132 }
133
134 #ifndef SERVER
135
136 /*
137         vertex_dirs: v3s16[4]
138 */
139 void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
140 {
141         /*
142                 If looked from outside the node towards the face, the corners are:
143                 0: bottom-right
144                 1: bottom-left
145                 2: top-left
146                 3: top-right
147         */
148         if(dir == v3s16(0,0,1))
149         {
150                 // If looking towards z+, this is the face that is behind
151                 // the center point, facing towards z+.
152                 vertex_dirs[0] = v3s16(-1,-1, 1);
153                 vertex_dirs[1] = v3s16( 1,-1, 1);
154                 vertex_dirs[2] = v3s16( 1, 1, 1);
155                 vertex_dirs[3] = v3s16(-1, 1, 1);
156         }
157         else if(dir == v3s16(0,0,-1))
158         {
159                 // faces towards Z-
160                 vertex_dirs[0] = v3s16( 1,-1,-1);
161                 vertex_dirs[1] = v3s16(-1,-1,-1);
162                 vertex_dirs[2] = v3s16(-1, 1,-1);
163                 vertex_dirs[3] = v3s16( 1, 1,-1);
164         }
165         else if(dir == v3s16(1,0,0))
166         {
167                 // faces towards X+
168                 vertex_dirs[0] = v3s16( 1,-1, 1);
169                 vertex_dirs[1] = v3s16( 1,-1,-1);
170                 vertex_dirs[2] = v3s16( 1, 1,-1);
171                 vertex_dirs[3] = v3s16( 1, 1, 1);
172         }
173         else if(dir == v3s16(-1,0,0))
174         {
175                 // faces towards X-
176                 vertex_dirs[0] = v3s16(-1,-1,-1);
177                 vertex_dirs[1] = v3s16(-1,-1, 1);
178                 vertex_dirs[2] = v3s16(-1, 1, 1);
179                 vertex_dirs[3] = v3s16(-1, 1,-1);
180         }
181         else if(dir == v3s16(0,1,0))
182         {
183                 // faces towards Y+ (assume Z- as "down" in texture)
184                 vertex_dirs[0] = v3s16( 1, 1,-1);
185                 vertex_dirs[1] = v3s16(-1, 1,-1);
186                 vertex_dirs[2] = v3s16(-1, 1, 1);
187                 vertex_dirs[3] = v3s16( 1, 1, 1);
188         }
189         else if(dir == v3s16(0,-1,0))
190         {
191                 // faces towards Y- (assume Z+ as "down" in texture)
192                 vertex_dirs[0] = v3s16( 1,-1, 1);
193                 vertex_dirs[1] = v3s16(-1,-1, 1);
194                 vertex_dirs[2] = v3s16(-1,-1,-1);
195                 vertex_dirs[3] = v3s16( 1,-1,-1);
196         }
197 }
198
199 inline video::SColor lightColor(u8 alpha, u8 light)
200 {
201         return video::SColor(alpha,light,light,light);
202 }
203
204 void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
205                 v3s16 dir, v3f scale, v3f posRelative_f,
206                 core::array<FastFace> &dest)
207 {
208         FastFace face;
209         
210         // Position is at the center of the cube.
211         v3f pos = p * BS;
212         posRelative_f *= BS;
213
214         v3f vertex_pos[4];
215         v3s16 vertex_dirs[4];
216         getNodeVertexDirs(dir, vertex_dirs);
217         for(u16 i=0; i<4; i++)
218         {
219                 vertex_pos[i] = v3f(
220                                 BS/2*vertex_dirs[i].X,
221                                 BS/2*vertex_dirs[i].Y,
222                                 BS/2*vertex_dirs[i].Z
223                 );
224         }
225
226         for(u16 i=0; i<4; i++)
227         {
228                 vertex_pos[i].X *= scale.X;
229                 vertex_pos[i].Y *= scale.Y;
230                 vertex_pos[i].Z *= scale.Z;
231                 vertex_pos[i] += pos + posRelative_f;
232         }
233
234         f32 abs_scale = 1.;
235         if     (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
236         else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
237         else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
238
239         v3f zerovector = v3f(0,0,0);
240         
241         u8 alpha = tile.alpha;
242         /*u8 alpha = 255;
243         if(tile.id == TILE_WATER)
244                 alpha = WATER_ALPHA;*/
245
246         float x0 = tile.texture.pos.X;
247         float y0 = tile.texture.pos.Y;
248         float w = tile.texture.size.X;
249         float h = tile.texture.size.Y;
250
251         /*video::SColor c = lightColor(alpha, li);
252
253         face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
254                         core::vector2d<f32>(x0+w*abs_scale, y0+h));
255         face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
256                         core::vector2d<f32>(x0, y0+h));
257         face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
258                         core::vector2d<f32>(x0, y0));
259         face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
260                         core::vector2d<f32>(x0+w*abs_scale, y0));*/
261
262         face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0),
263                         lightColor(alpha, li0),
264                         core::vector2d<f32>(x0+w*abs_scale, y0+h));
265         face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0),
266                         lightColor(alpha, li1),
267                         core::vector2d<f32>(x0, y0+h));
268         face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0),
269                         lightColor(alpha, li2),
270                         core::vector2d<f32>(x0, y0));
271         face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0),
272                         lightColor(alpha, li3),
273                         core::vector2d<f32>(x0+w*abs_scale, y0));
274
275         face.tile = tile;
276         //DEBUG
277         //f->tile = TILE_STONE;
278         
279         dest.push_back(face);
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 v3s16 dirs8[8] = {
377         v3s16(0,0,0),
378         v3s16(0,0,1),
379         v3s16(0,1,0),
380         v3s16(0,1,1),
381         v3s16(1,0,0),
382         v3s16(1,1,0),
383         v3s16(1,0,1),
384         v3s16(1,1,1),
385 };
386
387 // Calculate lighting at the XYZ- corner of p
388 u8 getSmoothLight(v3s16 p, VoxelManipulator &vmanip, u32 daynight_ratio)
389 {
390         u16 ambient_occlusion = 0;
391         u16 light = 0;
392         u16 light_count = 0;
393         for(u32 i=0; i<8; i++)
394         {
395                 MapNode n = vmanip.getNodeNoEx(p - dirs8[i]);
396                 if(content_features(n.d).param_type == CPT_LIGHT)
397                 {
398                         light += decode_light(n.getLightBlend(daynight_ratio));
399                         light_count++;
400                 }
401                 else
402                 {
403                         if(n.d != CONTENT_IGNORE)
404                                 ambient_occlusion++;
405                 }
406         }
407
408         if(light_count == 0)
409                 return 255;
410         
411         light /= light_count;
412
413         if(ambient_occlusion > 4)
414         {
415                 ambient_occlusion -= 4;
416                 light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
417         }
418
419         return light;
420 }
421
422 // Calculate lighting at the given corner of p
423 u8 getSmoothLight(v3s16 p, v3s16 corner,
424                 VoxelManipulator &vmanip, u32 daynight_ratio)
425 {
426         if(corner.X == 1) p.X += 1;
427         else              assert(corner.X == -1);
428         if(corner.Y == 1) p.Y += 1;
429         else              assert(corner.Y == -1);
430         if(corner.Z == 1) p.Z += 1;
431         else              assert(corner.Z == -1);
432         
433         return getSmoothLight(p, vmanip, daynight_ratio);
434 }
435
436 void getTileInfo(
437                 // Input:
438                 v3s16 blockpos_nodes,
439                 v3s16 p,
440                 v3s16 face_dir,
441                 u32 daynight_ratio,
442                 VoxelManipulator &vmanip,
443                 NodeModMap &temp_mods,
444                 bool smooth_lighting,
445                 // Output:
446                 bool &makes_face,
447                 v3s16 &p_corrected,
448                 v3s16 &face_dir_corrected,
449                 u8 *lights,
450                 TileSpec &tile
451         )
452 {
453         MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
454         MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
455         TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
456         TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
457         
458         // This is hackish
459         u8 content0 = getNodeContent(p, n0, temp_mods);
460         u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
461         u8 mf = face_contents(content0, content1);
462
463         if(mf == 0)
464         {
465                 makes_face = false;
466                 return;
467         }
468
469         makes_face = true;
470         
471         if(mf == 1)
472         {
473                 tile = tile0;
474                 p_corrected = p;
475                 face_dir_corrected = face_dir;
476         }
477         else
478         {
479                 tile = tile1;
480                 p_corrected = p + face_dir;
481                 face_dir_corrected = -face_dir;
482         }
483         
484         if(smooth_lighting == false)
485         {
486                 lights[0] = lights[1] = lights[2] = lights[3] =
487                                 decode_light(getFaceLight(daynight_ratio, n0, n1, face_dir));
488         }
489         else
490         {
491                 v3s16 vertex_dirs[4];
492                 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
493                 for(u16 i=0; i<4; i++)
494                 {
495                         lights[i] = getSmoothLight(blockpos_nodes + p_corrected,
496                                         vertex_dirs[i], vmanip, daynight_ratio);
497                 }
498         }
499         
500         return;
501 }
502
503 /*
504         startpos:
505         translate_dir: unit vector with only one of x, y or z
506         face_dir: unit vector with only one of x, y or z
507 */
508 void updateFastFaceRow(
509                 u32 daynight_ratio,
510                 v3f posRelative_f,
511                 v3s16 startpos,
512                 u16 length,
513                 v3s16 translate_dir,
514                 v3f translate_dir_f,
515                 v3s16 face_dir,
516                 v3f face_dir_f,
517                 core::array<FastFace> &dest,
518                 NodeModMap &temp_mods,
519                 VoxelManipulator &vmanip,
520                 v3s16 blockpos_nodes,
521                 bool smooth_lighting)
522 {
523         v3s16 p = startpos;
524         
525         u16 continuous_tiles_count = 0;
526         
527         bool makes_face;
528         v3s16 p_corrected;
529         v3s16 face_dir_corrected;
530         u8 lights[4];
531         TileSpec tile;
532         getTileInfo(blockpos_nodes, p, face_dir, daynight_ratio,
533                         vmanip, temp_mods, smooth_lighting,
534                         makes_face, p_corrected, face_dir_corrected, lights, tile);
535
536         for(u16 j=0; j<length; j++)
537         {
538                 // If tiling can be done, this is set to false in the next step
539                 bool next_is_different = true;
540                 
541                 v3s16 p_next;
542                 
543                 bool next_makes_face;
544                 v3s16 next_p_corrected;
545                 v3s16 next_face_dir_corrected;
546                 u8 next_lights[4];
547                 TileSpec next_tile;
548                 
549                 // If at last position, there is nothing to compare to and
550                 // the face must be drawn anyway
551                 if(j != length - 1)
552                 {
553                         p_next = p + translate_dir;
554                         
555                         getTileInfo(blockpos_nodes, p_next, face_dir, daynight_ratio,
556                                         vmanip, temp_mods, smooth_lighting,
557                                         next_makes_face, next_p_corrected,
558                                         next_face_dir_corrected, next_lights,
559                                         next_tile);
560                         
561                         if(next_makes_face == makes_face
562                                         && next_p_corrected == p_corrected
563                                         && next_face_dir_corrected == face_dir_corrected
564                                         && next_lights[0] == lights[0]
565                                         && next_lights[1] == lights[1]
566                                         && next_lights[2] == lights[2]
567                                         && next_lights[3] == lights[3]
568                                         && next_tile == tile)
569                         {
570                                 next_is_different = false;
571                         }
572                 }
573
574                 continuous_tiles_count++;
575                 
576                 // This is set to true if the texture doesn't allow more tiling
577                 bool end_of_texture = false;
578                 /*
579                         If there is no texture, it can be tiled infinitely.
580                         If tiled==0, it means the texture can be tiled infinitely.
581                         Otherwise check tiled agains continuous_tiles_count.
582                 */
583                 if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
584                 {
585                         if(tile.texture.tiled <= continuous_tiles_count)
586                                 end_of_texture = true;
587                 }
588                 
589                 // Do this to disable tiling textures
590                 //end_of_texture = true; //DEBUG
591                 
592                 if(next_is_different || end_of_texture)
593                 {
594                         /*
595                                 Create a face if there should be one
596                         */
597                         if(makes_face)
598                         {
599                                 // Floating point conversion of the position vector
600                                 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
601                                 // Center point of face (kind of)
602                                 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
603                                 v3f scale(1,1,1);
604
605                                 if(translate_dir.X != 0)
606                                 {
607                                         scale.X = continuous_tiles_count;
608                                 }
609                                 if(translate_dir.Y != 0)
610                                 {
611                                         scale.Y = continuous_tiles_count;
612                                 }
613                                 if(translate_dir.Z != 0)
614                                 {
615                                         scale.Z = continuous_tiles_count;
616                                 }
617                                 
618                                 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
619                                                 sp, face_dir_corrected, scale,
620                                                 posRelative_f, dest);
621                         }
622
623                         continuous_tiles_count = 0;
624                         
625                         makes_face = next_makes_face;
626                         p_corrected = next_p_corrected;
627                         face_dir_corrected = next_face_dir_corrected;
628                         lights[0] = next_lights[0];
629                         lights[1] = next_lights[1];
630                         lights[2] = next_lights[2];
631                         lights[3] = next_lights[3];
632                         tile = next_tile;
633                 }
634                 
635                 p = p_next;
636         }
637 }
638
639 /*
640         This is used because CMeshBuffer::append() is very slow
641 */
642 struct PreMeshBuffer
643 {
644         video::SMaterial material;
645         core::array<u16> indices;
646         core::array<video::S3DVertex> vertices;
647 };
648
649 class MeshCollector
650 {
651 public:
652         void append(
653                         video::SMaterial material,
654                         const video::S3DVertex* const vertices,
655                         u32 numVertices,
656                         const u16* const indices,
657                         u32 numIndices
658                 )
659         {
660                 PreMeshBuffer *p = NULL;
661                 for(u32 i=0; i<m_prebuffers.size(); i++)
662                 {
663                         PreMeshBuffer &pp = m_prebuffers[i];
664                         if(pp.material != material)
665                                 continue;
666
667                         p = &pp;
668                         break;
669                 }
670
671                 if(p == NULL)
672                 {
673                         PreMeshBuffer pp;
674                         pp.material = material;
675                         m_prebuffers.push_back(pp);
676                         p = &m_prebuffers[m_prebuffers.size()-1];
677                 }
678
679                 u32 vertex_count = p->vertices.size();
680                 for(u32 i=0; i<numIndices; i++)
681                 {
682                         u32 j = indices[i] + vertex_count;
683                         if(j > 65535)
684                         {
685                                 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
686                                 // NOTE: Fix is to just add an another MeshBuffer
687                         }
688                         p->indices.push_back(j);
689                 }
690                 for(u32 i=0; i<numVertices; i++)
691                 {
692                         p->vertices.push_back(vertices[i]);
693                 }
694         }
695
696         void fillMesh(scene::SMesh *mesh)
697         {
698                 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
699                                 <<" meshbuffers"<<std::endl;*/
700                 for(u32 i=0; i<m_prebuffers.size(); i++)
701                 {
702                         PreMeshBuffer &p = m_prebuffers[i];
703
704                         /*dstream<<"p.vertices.size()="<<p.vertices.size()
705                                         <<", p.indices.size()="<<p.indices.size()
706                                         <<std::endl;*/
707                         
708                         // Create meshbuffer
709                         
710                         // This is a "Standard MeshBuffer",
711                         // it's a typedeffed CMeshBuffer<video::S3DVertex>
712                         scene::SMeshBuffer *buf = new scene::SMeshBuffer();
713                         // Set material
714                         buf->Material = p.material;
715                         //((scene::SMeshBuffer*)buf)->Material = p.material;
716                         // Use VBO
717                         //buf->setHardwareMappingHint(scene::EHM_STATIC);
718                         // Add to mesh
719                         mesh->addMeshBuffer(buf);
720                         // Mesh grabbed it
721                         buf->drop();
722
723                         buf->append(p.vertices.pointer(), p.vertices.size(),
724                                         p.indices.pointer(), p.indices.size());
725                 }
726         }
727
728 private:
729         core::array<PreMeshBuffer> m_prebuffers;
730 };
731
732 void makeCuboid(video::SMaterial &material, MeshCollector *collector,
733         AtlasPointer* pa, video::SColor &c,
734         v3f &pos, f32 rx, f32 ry, f32 rz)
735 {
736         video::S3DVertex v[4] =
737         {
738                 video::S3DVertex(0,0,0, 0,0,0, c,
739                         pa->x0(), pa->y1()),
740                 video::S3DVertex(0,0,0, 0,0,0, c,
741                         pa->x1(), pa->y1()),
742                 video::S3DVertex(0,0,0, 0,0,0, c,
743                         pa->x1(), pa->y0()),
744                 video::S3DVertex(0,0,0, 0,0,0, c,
745                         pa->x0(), pa->y0())
746         };
747
748         for(int i=0;i<6;i++)
749         {
750                 switch(i)
751                 {
752                         case 0:
753                                 v[0].Pos.X=-rx; v[0].Pos.Y= ry; v[0].Pos.Z=-rz;
754                                 v[1].Pos.X=-rx; v[1].Pos.Y= ry; v[1].Pos.Z= rz;
755                                 v[2].Pos.X= rx; v[2].Pos.Y= ry; v[2].Pos.Z= rz;
756                                 v[3].Pos.X= rx; v[3].Pos.Y= ry, v[3].Pos.Z=-rz;
757                                 break;
758                         case 1:
759                                 v[0].Pos.X=-rx; v[0].Pos.Y= ry; v[0].Pos.Z=-rz;
760                                 v[1].Pos.X= rx; v[1].Pos.Y= ry; v[1].Pos.Z=-rz;
761                                 v[2].Pos.X= rx; v[2].Pos.Y=-ry; v[2].Pos.Z=-rz;
762                                 v[3].Pos.X=-rx; v[3].Pos.Y=-ry, v[3].Pos.Z=-rz;
763                                 break;
764                         case 2:
765                                 v[0].Pos.X= rx; v[0].Pos.Y= ry; v[0].Pos.Z=-rz;
766                                 v[1].Pos.X= rx; v[1].Pos.Y= ry; v[1].Pos.Z= rz;
767                                 v[2].Pos.X= rx; v[2].Pos.Y=-ry; v[2].Pos.Z= rz;
768                                 v[3].Pos.X= rx; v[3].Pos.Y=-ry, v[3].Pos.Z=-rz;
769                                 break;
770                         case 3:
771                                 v[0].Pos.X= rx; v[0].Pos.Y= ry; v[0].Pos.Z= rz;
772                                 v[1].Pos.X=-rx; v[1].Pos.Y= ry; v[1].Pos.Z= rz;
773                                 v[2].Pos.X=-rx; v[2].Pos.Y=-ry; v[2].Pos.Z= rz;
774                                 v[3].Pos.X= rx; v[3].Pos.Y=-ry, v[3].Pos.Z= rz;
775                                 break;
776                         case 4:
777                                 v[0].Pos.X=-rx; v[0].Pos.Y= ry; v[0].Pos.Z= rz;
778                                 v[1].Pos.X=-rx; v[1].Pos.Y= ry; v[1].Pos.Z=-rz;
779                                 v[2].Pos.X=-rx; v[2].Pos.Y=-ry; v[2].Pos.Z=-rz;
780                                 v[3].Pos.X=-rx; v[3].Pos.Y=-ry, v[3].Pos.Z= rz;
781                                 break;
782                         case 5:
783                                 v[0].Pos.X= rx; v[0].Pos.Y=-ry; v[0].Pos.Z= rz;
784                                 v[1].Pos.X=-rx; v[1].Pos.Y=-ry; v[1].Pos.Z= rz;
785                                 v[2].Pos.X=-rx; v[2].Pos.Y=-ry; v[2].Pos.Z=-rz;
786                                 v[3].Pos.X= rx; v[3].Pos.Y=-ry, v[3].Pos.Z=-rz;
787                                 break;
788                 }
789                 for(u16 i=0; i<4; i++)
790                         v[i].Pos += pos;
791                 u16 indices[] = {0,1,2,2,3,0};
792                 collector->append(material, v, 4, indices, 6);
793
794         }
795
796 }
797
798 scene::SMesh* makeMapBlockMesh(MeshMakeData *data)
799 {
800         // 4-21ms for MAP_BLOCKSIZE=16
801         // 24-155ms for MAP_BLOCKSIZE=32
802         //TimeTaker timer1("makeMapBlockMesh()");
803
804         core::array<FastFace> fastfaces_new;
805
806         v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
807         
808         // floating point conversion
809         v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
810         
811         /*
812                 Some settings
813         */
814         bool new_style_water = g_settings.getBool("new_style_water");
815         bool new_style_leaves = g_settings.getBool("new_style_leaves");
816         bool smooth_lighting = g_settings.getBool("smooth_lighting");
817         
818         float node_water_level = 1.0;
819         if(new_style_water)
820                 node_water_level = 0.85;
821         
822         /*
823                 We are including the faces of the trailing edges of the block.
824                 This means that when something changes, the caller must
825                 also update the meshes of the blocks at the leading edges.
826
827                 NOTE: This is the slowest part of this method.
828         */
829         
830         {
831                 // 4-23ms for MAP_BLOCKSIZE=16
832                 //TimeTaker timer2("updateMesh() collect");
833
834                 /*
835                         Go through every y,z and get top(y+) faces in rows of x+
836                 */
837                 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
838                         for(s16 z=0; z<MAP_BLOCKSIZE; z++){
839                                 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
840                                                 v3s16(0,y,z), MAP_BLOCKSIZE,
841                                                 v3s16(1,0,0), //dir
842                                                 v3f  (1,0,0),
843                                                 v3s16(0,1,0), //face dir
844                                                 v3f  (0,1,0),
845                                                 fastfaces_new,
846                                                 data->m_temp_mods,
847                                                 data->m_vmanip,
848                                                 blockpos_nodes,
849                                                 smooth_lighting);
850                         }
851                 }
852                 /*
853                         Go through every x,y and get right(x+) faces in rows of z+
854                 */
855                 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
856                         for(s16 y=0; y<MAP_BLOCKSIZE; y++){
857                                 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
858                                                 v3s16(x,y,0), MAP_BLOCKSIZE,
859                                                 v3s16(0,0,1),
860                                                 v3f  (0,0,1),
861                                                 v3s16(1,0,0),
862                                                 v3f  (1,0,0),
863                                                 fastfaces_new,
864                                                 data->m_temp_mods,
865                                                 data->m_vmanip,
866                                                 blockpos_nodes,
867                                                 smooth_lighting);
868                         }
869                 }
870                 /*
871                         Go through every y,z and get back(z+) faces in rows of x+
872                 */
873                 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
874                         for(s16 y=0; y<MAP_BLOCKSIZE; y++){
875                                 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
876                                                 v3s16(0,y,z), MAP_BLOCKSIZE,
877                                                 v3s16(1,0,0),
878                                                 v3f  (1,0,0),
879                                                 v3s16(0,0,1),
880                                                 v3f  (0,0,1),
881                                                 fastfaces_new,
882                                                 data->m_temp_mods,
883                                                 data->m_vmanip,
884                                                 blockpos_nodes,
885                                                 smooth_lighting);
886                         }
887                 }
888         }
889
890         // End of slow part
891
892         /*
893                 Convert FastFaces to SMesh
894         */
895
896         MeshCollector collector;
897
898         if(fastfaces_new.size() > 0)
899         {
900                 // avg 0ms (100ms spikes when loading textures the first time)
901                 //TimeTaker timer2("updateMesh() mesh building");
902
903                 video::SMaterial material;
904                 material.setFlag(video::EMF_LIGHTING, false);
905                 material.setFlag(video::EMF_BILINEAR_FILTER, false);
906                 material.setFlag(video::EMF_FOG_ENABLE, true);
907                 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
908                 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
909
910                 for(u32 i=0; i<fastfaces_new.size(); i++)
911                 {
912                         FastFace &f = fastfaces_new[i];
913
914                         const u16 indices[] = {0,1,2,2,3,0};
915                         const u16 indices_alternate[] = {0,1,3,2,3,1};
916                         
917                         video::ITexture *texture = f.tile.texture.atlas;
918                         if(texture == NULL)
919                                 continue;
920
921                         material.setTexture(0, texture);
922                         
923                         f.tile.applyMaterialOptions(material);
924
925                         const u16 *indices_p = indices;
926                         
927                         /*
928                                 Revert triangles for nicer looking gradient if vertices
929                                 1 and 3 have same color or 0 and 2 have different color.
930                         */
931                         if(f.vertices[0].Color != f.vertices[2].Color
932                                         || f.vertices[1].Color == f.vertices[3].Color)
933                                 indices_p = indices_alternate;
934                         
935                         collector.append(material, f.vertices, 4, indices_p, 6);
936                 }
937         }
938
939         /*
940                 Add special graphics:
941                 - torches
942                 - flowing water
943         */
944
945         // 0ms
946         //TimeTaker timer2("updateMesh() adding special stuff");
947
948         // Flowing water material
949         video::SMaterial material_water1;
950         material_water1.setFlag(video::EMF_LIGHTING, false);
951         material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
952         material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
953         material_water1.setFlag(video::EMF_FOG_ENABLE, true);
954         material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
955         AtlasPointer pa_water1 = g_texturesource->getTexture(
956                         g_texturesource->getTextureId("water.png"));
957         material_water1.setTexture(0, pa_water1.atlas);
958
959         // New-style leaves material
960         video::SMaterial material_leaves1;
961         material_leaves1.setFlag(video::EMF_LIGHTING, false);
962         //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
963         material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
964         material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
965         material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
966         AtlasPointer pa_leaves1 = g_texturesource->getTexture(
967                         g_texturesource->getTextureId("leaves.png"));
968         material_leaves1.setTexture(0, pa_leaves1.atlas);
969
970         // Glass material
971         video::SMaterial material_glass;
972         material_glass.setFlag(video::EMF_LIGHTING, false);
973         material_glass.setFlag(video::EMF_BILINEAR_FILTER, false);
974         material_glass.setFlag(video::EMF_FOG_ENABLE, true);
975         material_glass.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
976         AtlasPointer pa_glass = g_texturesource->getTexture(
977                         g_texturesource->getTextureId("glass.png"));
978         material_glass.setTexture(0, pa_glass.atlas);
979
980         // Wood material
981         video::SMaterial material_wood;
982         material_wood.setFlag(video::EMF_LIGHTING, false);
983         material_wood.setFlag(video::EMF_BILINEAR_FILTER, false);
984         material_wood.setFlag(video::EMF_FOG_ENABLE, true);
985         material_wood.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
986         AtlasPointer pa_wood = g_texturesource->getTexture(
987                         g_texturesource->getTextureId("wood.png"));
988         material_wood.setTexture(0, pa_wood.atlas);
989
990         // Papyrus material
991         video::SMaterial material_papyrus;
992         material_papyrus.setFlag(video::EMF_LIGHTING, false);
993         material_papyrus.setFlag(video::EMF_BILINEAR_FILTER, false);
994         material_papyrus.setFlag(video::EMF_FOG_ENABLE, true);
995         material_papyrus.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
996         AtlasPointer pa_papyrus = g_texturesource->getTexture(
997                         g_texturesource->getTextureId("papyrus.png"));
998         material_papyrus.setTexture(0, pa_papyrus.atlas);
999
1000         for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1001         for(s16 y=0; y<MAP_BLOCKSIZE; y++)
1002         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1003         {
1004                 v3s16 p(x,y,z);
1005
1006                 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p);
1007                 
1008                 /*
1009                         Add torches to mesh
1010                 */
1011                 if(n.d == CONTENT_TORCH)
1012                 {
1013                         video::SColor c(255,255,255,255);
1014
1015                         // Wall at X+ of node
1016                         video::S3DVertex vertices[4] =
1017                         {
1018                                 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
1019                                 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
1020                                 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
1021                                 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
1022                         };
1023
1024                         v3s16 dir = unpackDir(n.dir);
1025
1026                         for(s32 i=0; i<4; i++)
1027                         {
1028                                 if(dir == v3s16(1,0,0))
1029                                         vertices[i].Pos.rotateXZBy(0);
1030                                 if(dir == v3s16(-1,0,0))
1031                                         vertices[i].Pos.rotateXZBy(180);
1032                                 if(dir == v3s16(0,0,1))
1033                                         vertices[i].Pos.rotateXZBy(90);
1034                                 if(dir == v3s16(0,0,-1))
1035                                         vertices[i].Pos.rotateXZBy(-90);
1036                                 if(dir == v3s16(0,-1,0))
1037                                         vertices[i].Pos.rotateXZBy(45);
1038                                 if(dir == v3s16(0,1,0))
1039                                         vertices[i].Pos.rotateXZBy(-45);
1040
1041                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1042                         }
1043
1044                         // Set material
1045                         video::SMaterial material;
1046                         material.setFlag(video::EMF_LIGHTING, false);
1047                         material.setFlag(video::EMF_BACK_FACE_CULLING, false);
1048                         material.setFlag(video::EMF_BILINEAR_FILTER, false);
1049                         //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
1050                         material.MaterialType
1051                                         = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1052
1053                         if(dir == v3s16(0,-1,0))
1054                                 material.setTexture(0,
1055                                                 g_texturesource->getTextureRaw("torch_on_floor.png"));
1056                         else if(dir == v3s16(0,1,0))
1057                                 material.setTexture(0,
1058                                                 g_texturesource->getTextureRaw("torch_on_ceiling.png"));
1059                         // For backwards compatibility
1060                         else if(dir == v3s16(0,0,0))
1061                                 material.setTexture(0,
1062                                                 g_texturesource->getTextureRaw("torch_on_floor.png"));
1063                         else
1064                                 material.setTexture(0, 
1065                                                 g_texturesource->getTextureRaw("torch.png"));
1066
1067                         u16 indices[] = {0,1,2,2,3,0};
1068                         // Add to mesh collector
1069                         collector.append(material, vertices, 4, indices, 6);
1070                 }
1071                 /*
1072                         Signs on walls
1073                 */
1074                 if(n.d == CONTENT_SIGN_WALL)
1075                 {
1076                         u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1077                         video::SColor c(255,l,l,l);
1078                                 
1079                         float d = (float)BS/16;
1080                         // Wall at X+ of node
1081                         video::S3DVertex vertices[4] =
1082                         {
1083                                 video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1),
1084                                 video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1),
1085                                 video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0),
1086                                 video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
1087                         };
1088
1089                         v3s16 dir = unpackDir(n.dir);
1090
1091                         for(s32 i=0; i<4; i++)
1092                         {
1093                                 if(dir == v3s16(1,0,0))
1094                                         vertices[i].Pos.rotateXZBy(0);
1095                                 if(dir == v3s16(-1,0,0))
1096                                         vertices[i].Pos.rotateXZBy(180);
1097                                 if(dir == v3s16(0,0,1))
1098                                         vertices[i].Pos.rotateXZBy(90);
1099                                 if(dir == v3s16(0,0,-1))
1100                                         vertices[i].Pos.rotateXZBy(-90);
1101                                 if(dir == v3s16(0,-1,0))
1102                                         vertices[i].Pos.rotateXYBy(-90);
1103                                 if(dir == v3s16(0,1,0))
1104                                         vertices[i].Pos.rotateXYBy(90);
1105
1106                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1107                         }
1108
1109                         // Set material
1110                         video::SMaterial material;
1111                         material.setFlag(video::EMF_LIGHTING, false);
1112                         material.setFlag(video::EMF_BACK_FACE_CULLING, false);
1113                         material.setFlag(video::EMF_BILINEAR_FILTER, false);
1114                         material.setFlag(video::EMF_FOG_ENABLE, true);
1115                         //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
1116                         material.MaterialType
1117                                         = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1118
1119                         material.setTexture(0, 
1120                                         g_texturesource->getTextureRaw("sign_wall.png"));
1121
1122                         u16 indices[] = {0,1,2,2,3,0};
1123                         // Add to mesh collector
1124                         collector.append(material, vertices, 4, indices, 6);
1125                 }
1126                 /*
1127                         Add flowing water to mesh
1128                 */
1129                 else if(n.d == CONTENT_WATER)
1130                 {
1131                         bool top_is_water = false;
1132                         MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1133                         if(ntop.d == CONTENT_WATER || ntop.d == CONTENT_WATERSOURCE)
1134                                 top_is_water = true;
1135                         
1136                         u8 l = 0;
1137                         // Use the light of the node on top if possible
1138                         if(content_features(ntop.d).param_type == CPT_LIGHT)
1139                                 l = decode_light(ntop.getLightBlend(data->m_daynight_ratio));
1140                         // Otherwise use the light of this node (the water)
1141                         else
1142                                 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1143                         video::SColor c(WATER_ALPHA,l,l,l);
1144                         
1145                         // Neighbor water levels (key = relative position)
1146                         // Includes current node
1147                         core::map<v3s16, f32> neighbor_levels;
1148                         core::map<v3s16, u8> neighbor_contents;
1149                         core::map<v3s16, u8> neighbor_flags;
1150                         const u8 neighborflag_top_is_water = 0x01;
1151                         v3s16 neighbor_dirs[9] = {
1152                                 v3s16(0,0,0),
1153                                 v3s16(0,0,1),
1154                                 v3s16(0,0,-1),
1155                                 v3s16(1,0,0),
1156                                 v3s16(-1,0,0),
1157                                 v3s16(1,0,1),
1158                                 v3s16(-1,0,-1),
1159                                 v3s16(1,0,-1),
1160                                 v3s16(-1,0,1),
1161                         };
1162                         for(u32 i=0; i<9; i++)
1163                         {
1164                                 u8 content = CONTENT_AIR;
1165                                 float level = -0.5 * BS;
1166                                 u8 flags = 0;
1167                                 // Check neighbor
1168                                 v3s16 p2 = p + neighbor_dirs[i];
1169                                 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1170                                 if(n2.d != CONTENT_IGNORE)
1171                                 {
1172                                         content = n2.d;
1173
1174                                         if(n2.d == CONTENT_WATERSOURCE)
1175                                                 level = (-0.5+node_water_level) * BS;
1176                                         else if(n2.d == CONTENT_WATER)
1177                                                 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
1178                                                                 * node_water_level) * BS;
1179
1180                                         // Check node above neighbor.
1181                                         // NOTE: This doesn't get executed if neighbor
1182                                         //       doesn't exist
1183                                         p2.Y += 1;
1184                                         n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1185                                         if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
1186                                                 flags |= neighborflag_top_is_water;
1187                                 }
1188                                 
1189                                 neighbor_levels.insert(neighbor_dirs[i], level);
1190                                 neighbor_contents.insert(neighbor_dirs[i], content);
1191                                 neighbor_flags.insert(neighbor_dirs[i], flags);
1192                         }
1193
1194                         //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
1195                         //float water_level = neighbor_levels[v3s16(0,0,0)];
1196
1197                         // Corner heights (average between four waters)
1198                         f32 corner_levels[4];
1199                         
1200                         v3s16 halfdirs[4] = {
1201                                 v3s16(0,0,0),
1202                                 v3s16(1,0,0),
1203                                 v3s16(1,0,1),
1204                                 v3s16(0,0,1),
1205                         };
1206                         for(u32 i=0; i<4; i++)
1207                         {
1208                                 v3s16 cornerdir = halfdirs[i];
1209                                 float cornerlevel = 0;
1210                                 u32 valid_count = 0;
1211                                 for(u32 j=0; j<4; j++)
1212                                 {
1213                                         v3s16 neighbordir = cornerdir - halfdirs[j];
1214                                         u8 content = neighbor_contents[neighbordir];
1215                                         // Special case for source nodes
1216                                         if(content == CONTENT_WATERSOURCE)
1217                                         {
1218                                                 cornerlevel = (-0.5+node_water_level)*BS;
1219                                                 valid_count = 1;
1220                                                 break;
1221                                         }
1222                                         else if(content == CONTENT_WATER)
1223                                         {
1224                                                 cornerlevel += neighbor_levels[neighbordir];
1225                                                 valid_count++;
1226                                         }
1227                                         else if(content == CONTENT_AIR)
1228                                         {
1229                                                 cornerlevel += -0.5*BS;
1230                                                 valid_count++;
1231                                         }
1232                                 }
1233                                 if(valid_count > 0)
1234                                         cornerlevel /= valid_count;
1235                                 corner_levels[i] = cornerlevel;
1236                         }
1237
1238                         /*
1239                                 Generate sides
1240                         */
1241
1242                         v3s16 side_dirs[4] = {
1243                                 v3s16(1,0,0),
1244                                 v3s16(-1,0,0),
1245                                 v3s16(0,0,1),
1246                                 v3s16(0,0,-1),
1247                         };
1248                         s16 side_corners[4][2] = {
1249                                 {1, 2},
1250                                 {3, 0},
1251                                 {2, 3},
1252                                 {0, 1},
1253                         };
1254                         for(u32 i=0; i<4; i++)
1255                         {
1256                                 v3s16 dir = side_dirs[i];
1257
1258                                 /*
1259                                         If our topside is water and neighbor's topside
1260                                         is water, don't draw side face
1261                                 */
1262                                 if(top_is_water &&
1263                                                 neighbor_flags[dir] & neighborflag_top_is_water)
1264                                         continue;
1265
1266                                 u8 neighbor_content = neighbor_contents[dir];
1267                                 
1268                                 // Don't draw face if neighbor is not air or water
1269                                 if(neighbor_content != CONTENT_AIR
1270                                                 && neighbor_content != CONTENT_WATER)
1271                                         continue;
1272                                 
1273                                 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
1274                                 
1275                                 // Don't draw any faces if neighbor is water and top is water
1276                                 if(neighbor_is_water == true && top_is_water == false)
1277                                         continue;
1278                                 
1279                                 video::S3DVertex vertices[4] =
1280                                 {
1281                                         /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1282                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1283                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1284                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1285                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1286                                                         pa_water1.x0(), pa_water1.y1()),
1287                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1288                                                         pa_water1.x1(), pa_water1.y1()),
1289                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1290                                                         pa_water1.x1(), pa_water1.y0()),
1291                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1292                                                         pa_water1.x0(), pa_water1.y0()),
1293                                 };
1294                                 
1295                                 /*
1296                                         If our topside is water, set upper border of face
1297                                         at upper border of node
1298                                 */
1299                                 if(top_is_water)
1300                                 {
1301                                         vertices[2].Pos.Y = 0.5*BS;
1302                                         vertices[3].Pos.Y = 0.5*BS;
1303                                 }
1304                                 /*
1305                                         Otherwise upper position of face is corner levels
1306                                 */
1307                                 else
1308                                 {
1309                                         vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1310                                         vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1311                                 }
1312                                 
1313                                 /*
1314                                         If neighbor is water, lower border of face is corner
1315                                         water levels
1316                                 */
1317                                 if(neighbor_is_water)
1318                                 {
1319                                         vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1320                                         vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1321                                 }
1322                                 /*
1323                                         If neighbor is not water, lower border of face is
1324                                         lower border of node
1325                                 */
1326                                 else
1327                                 {
1328                                         vertices[0].Pos.Y = -0.5*BS;
1329                                         vertices[1].Pos.Y = -0.5*BS;
1330                                 }
1331                                 
1332                                 for(s32 j=0; j<4; j++)
1333                                 {
1334                                         if(dir == v3s16(0,0,1))
1335                                                 vertices[j].Pos.rotateXZBy(0);
1336                                         if(dir == v3s16(0,0,-1))
1337                                                 vertices[j].Pos.rotateXZBy(180);
1338                                         if(dir == v3s16(-1,0,0))
1339                                                 vertices[j].Pos.rotateXZBy(90);
1340                                         if(dir == v3s16(1,0,-0))
1341                                                 vertices[j].Pos.rotateXZBy(-90);
1342
1343                                         vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
1344                                 }
1345
1346                                 u16 indices[] = {0,1,2,2,3,0};
1347                                 // Add to mesh collector
1348                                 collector.append(material_water1, vertices, 4, indices, 6);
1349                         }
1350                         
1351                         /*
1352                                 Generate top side, if appropriate
1353                         */
1354                         
1355                         if(top_is_water == false)
1356                         {
1357                                 video::S3DVertex vertices[4] =
1358                                 {
1359                                         /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1360                                         video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1361                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1362                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1363                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1364                                                         pa_water1.x0(), pa_water1.y1()),
1365                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1366                                                         pa_water1.x1(), pa_water1.y1()),
1367                                         video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1368                                                         pa_water1.x1(), pa_water1.y0()),
1369                                         video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1370                                                         pa_water1.x0(), pa_water1.y0()),
1371                                 };
1372                                 
1373                                 // This fixes a strange bug
1374                                 s32 corner_resolve[4] = {3,2,1,0};
1375
1376                                 for(s32 i=0; i<4; i++)
1377                                 {
1378                                         //vertices[i].Pos.Y += water_level;
1379                                         //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1380                                         s32 j = corner_resolve[i];
1381                                         vertices[i].Pos.Y += corner_levels[j];
1382                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1383                                 }
1384
1385                                 u16 indices[] = {0,1,2,2,3,0};
1386                                 // Add to mesh collector
1387                                 collector.append(material_water1, vertices, 4, indices, 6);
1388                         }
1389                 }
1390                 /*
1391                         Add water sources to mesh if using new style
1392                 */
1393                 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1394                 {
1395                         //bool top_is_water = false;
1396                         bool top_is_air = false;
1397                         MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1398                         /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1399                                 top_is_water = true;*/
1400                         if(n.d == CONTENT_AIR)
1401                                 top_is_air = true;
1402                         
1403                         /*if(top_is_water == true)
1404                                 continue;*/
1405                         if(top_is_air == false)
1406                                 continue;
1407
1408                         u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1409                         video::SColor c(WATER_ALPHA,l,l,l);
1410                         
1411                         video::S3DVertex vertices[4] =
1412                         {
1413                                 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1414                                 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1415                                 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1416                                 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1417                                 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1418                                                 pa_water1.x0(), pa_water1.y1()),
1419                                 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1420                                                 pa_water1.x1(), pa_water1.y1()),
1421                                 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1422                                                 pa_water1.x1(), pa_water1.y0()),
1423                                 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1424                                                 pa_water1.x0(), pa_water1.y0()),
1425                         };
1426
1427                         for(s32 i=0; i<4; i++)
1428                         {
1429                                 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1430                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1431                         }
1432
1433                         u16 indices[] = {0,1,2,2,3,0};
1434                         // Add to mesh collector
1435                         collector.append(material_water1, vertices, 4, indices, 6);
1436                 }
1437                 /*
1438                         Add leaves if using new style
1439                 */
1440                 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1441                 {
1442                         /*u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));*/
1443                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1444                         video::SColor c(255,l,l,l);
1445
1446                         for(u32 j=0; j<6; j++)
1447                         {
1448                                 video::S3DVertex vertices[4] =
1449                                 {
1450                                         /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1451                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1452                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1453                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1454                                         video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1455                                                 pa_leaves1.x0(), pa_leaves1.y1()),
1456                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1457                                                 pa_leaves1.x1(), pa_leaves1.y1()),
1458                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1459                                                 pa_leaves1.x1(), pa_leaves1.y0()),
1460                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1461                                                 pa_leaves1.x0(), pa_leaves1.y0()),
1462                                 };
1463
1464                                 if(j == 0)
1465                                 {
1466                                         for(u16 i=0; i<4; i++)
1467                                                 vertices[i].Pos.rotateXZBy(0);
1468                                 }
1469                                 else if(j == 1)
1470                                 {
1471                                         for(u16 i=0; i<4; i++)
1472                                                 vertices[i].Pos.rotateXZBy(180);
1473                                 }
1474                                 else if(j == 2)
1475                                 {
1476                                         for(u16 i=0; i<4; i++)
1477                                                 vertices[i].Pos.rotateXZBy(-90);
1478                                 }
1479                                 else if(j == 3)
1480                                 {
1481                                         for(u16 i=0; i<4; i++)
1482                                                 vertices[i].Pos.rotateXZBy(90);
1483                                 }
1484                                 else if(j == 4)
1485                                 {
1486                                         for(u16 i=0; i<4; i++)
1487                                                 vertices[i].Pos.rotateYZBy(-90);
1488                                 }
1489                                 else if(j == 5)
1490                                 {
1491                                         for(u16 i=0; i<4; i++)
1492                                                 vertices[i].Pos.rotateYZBy(90);
1493                                 }
1494
1495                                 for(u16 i=0; i<4; i++)
1496                                 {
1497                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1498                                 }
1499
1500                                 u16 indices[] = {0,1,2,2,3,0};
1501                                 // Add to mesh collector
1502                                 collector.append(material_leaves1, vertices, 4, indices, 6);
1503                         }
1504                 }
1505                 /*
1506                         Add glass
1507                 */
1508                 else if(n.d == CONTENT_GLASS)
1509                 {
1510                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1511                         video::SColor c(255,l,l,l);
1512
1513                         for(u32 j=0; j<6; j++)
1514                         {
1515                                 video::S3DVertex vertices[4] =
1516                                 {
1517                                         video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1518                                                 pa_glass.x0(), pa_glass.y1()),
1519                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1520                                                 pa_glass.x1(), pa_glass.y1()),
1521                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1522                                                 pa_glass.x1(), pa_glass.y0()),
1523                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1524                                                 pa_glass.x0(), pa_glass.y0()),
1525                                 };
1526
1527                                 if(j == 0)
1528                                 {
1529                                         for(u16 i=0; i<4; i++)
1530                                                 vertices[i].Pos.rotateXZBy(0);
1531                                 }
1532                                 else if(j == 1)
1533                                 {
1534                                         for(u16 i=0; i<4; i++)
1535                                                 vertices[i].Pos.rotateXZBy(180);
1536                                 }
1537                                 else if(j == 2)
1538                                 {
1539                                         for(u16 i=0; i<4; i++)
1540                                                 vertices[i].Pos.rotateXZBy(-90);
1541                                 }
1542                                 else if(j == 3)
1543                                 {
1544                                         for(u16 i=0; i<4; i++)
1545                                                 vertices[i].Pos.rotateXZBy(90);
1546                                 }
1547                                 else if(j == 4)
1548                                 {
1549                                         for(u16 i=0; i<4; i++)
1550                                                 vertices[i].Pos.rotateYZBy(-90);
1551                                 }
1552                                 else if(j == 5)
1553                                 {
1554                                         for(u16 i=0; i<4; i++)
1555                                                 vertices[i].Pos.rotateYZBy(90);
1556                                 }
1557
1558                                 for(u16 i=0; i<4; i++)
1559                                 {
1560                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1561                                 }
1562
1563                                 u16 indices[] = {0,1,2,2,3,0};
1564                                 // Add to mesh collector
1565                                 collector.append(material_glass, vertices, 4, indices, 6);
1566                         }
1567                 }
1568                 /*
1569                         Add fence
1570                 */
1571                 else if(n.d == CONTENT_FENCE)
1572                 {
1573                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1574                         video::SColor c(255,l,l,l);
1575
1576                         const f32 post_rad=(f32)BS/(32.0/4.0);
1577                         const f32 bar_rad=(f32)BS/(32.0/2.0);
1578                         const f32 bar_len=(f32)(BS/2)-post_rad;
1579
1580                         // The post - always present
1581                         v3f pos = intToFloat(p+blockpos_nodes, BS);
1582                         makeCuboid(material_wood, &collector,
1583                                 &pa_wood, c, pos,
1584                                 post_rad,BS/2,post_rad);
1585
1586                         // Now a section of fence, +X, if there's a post there
1587                         v3s16 p2 = p;
1588                         p2.X++;
1589                         MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1590                         if(n2.d == CONTENT_FENCE)
1591                         {
1592                                 pos = intToFloat(p+blockpos_nodes, BS);
1593                                 pos.X += BS/2;
1594                                 pos.Y += 3*bar_rad;
1595                                 makeCuboid(material_wood, &collector,
1596                                         &pa_wood, c, pos,
1597                                         bar_len,bar_rad,bar_rad);
1598
1599                                 pos.Y -= 6*bar_rad;
1600                                 makeCuboid(material_wood, &collector,
1601                                         &pa_wood, c, pos,
1602                                         bar_len,bar_rad,bar_rad);
1603                         }
1604
1605                         // Now a section of fence, +Z, if there's a post there
1606                         p2 = p;
1607                         p2.Z++;
1608                         n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1609                         if(n2.d == CONTENT_FENCE)
1610                         {
1611                                 pos = intToFloat(p+blockpos_nodes, BS);
1612                                 pos.Z += BS/2;
1613                                 pos.Y += 3*bar_rad;
1614                                 makeCuboid(material_wood, &collector,
1615                                         &pa_wood, c, pos,
1616                                         bar_rad,bar_rad,bar_len);
1617                                 pos.Y -= 6*bar_rad;
1618                                 makeCuboid(material_wood, &collector,
1619                                         &pa_wood, c, pos,
1620                                         bar_rad,bar_rad,bar_len);
1621
1622                         }
1623
1624                 }
1625                 else if(n.d == CONTENT_PAPYRUS)
1626                 {
1627                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1628                         video::SColor c(255,l,l,l);
1629
1630                         for(u32 j=0; j<4; j++)
1631                         {
1632                                 video::S3DVertex vertices[4] =
1633                                 {
1634                                         video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c,
1635                                                 pa_papyrus.x0(), pa_papyrus.y1()),
1636                                         video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c,
1637                                                 pa_papyrus.x1(), pa_papyrus.y1()),
1638                                         video::S3DVertex(BS/2,BS/2,0, 0,0,0, c,
1639                                                 pa_papyrus.x1(), pa_papyrus.y0()),
1640                                         video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c,
1641                                                 pa_papyrus.x0(), pa_papyrus.y0()),
1642                                 };
1643
1644                                 if(j == 0)
1645                                 {
1646                                         for(u16 i=0; i<4; i++)
1647                                                 vertices[i].Pos.rotateXZBy(45);
1648                                 }
1649                                 else if(j == 1)
1650                                 {
1651                                         for(u16 i=0; i<4; i++)
1652                                                 vertices[i].Pos.rotateXZBy(-45);
1653                                 }
1654                                 else if(j == 2)
1655                                 {
1656                                         for(u16 i=0; i<4; i++)
1657                                                 vertices[i].Pos.rotateXZBy(135);
1658                                 }
1659                                 else if(j == 3)
1660                                 {
1661                                         for(u16 i=0; i<4; i++)
1662                                                 vertices[i].Pos.rotateXZBy(-135);
1663                                 }
1664
1665                                 for(u16 i=0; i<4; i++)
1666                                 {
1667                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1668                                 }
1669
1670                                 u16 indices[] = {0,1,2,2,3,0};
1671                                 // Add to mesh collector
1672                                 collector.append(material_papyrus, vertices, 4, indices, 6);
1673                         }
1674                 }
1675                 else if(n.d == CONTENT_RAIL)
1676                 {
1677                         u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1678                         video::SColor c(255,l,l,l);
1679
1680                         bool is_rail_x [] = { false, false };  /* x-1, x+1 */
1681                         bool is_rail_z [] = { false, false };  /* z-1, z+1 */
1682
1683                         MapNode n_minus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1,y,z));
1684                         MapNode n_plus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1,y,z));
1685                         MapNode n_minus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z-1));
1686                         MapNode n_plus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z+1));
1687
1688                         if(n_minus_x.d == CONTENT_RAIL)
1689                                 is_rail_x[0] = true;
1690                         if(n_plus_x.d == CONTENT_RAIL)
1691                                 is_rail_x[1] = true;
1692                         if(n_minus_z.d == CONTENT_RAIL)
1693                                 is_rail_z[0] = true;
1694                         if(n_plus_z.d == CONTENT_RAIL)
1695                                 is_rail_z[1] = true;
1696
1697                         float d = (float)BS/16;
1698                         video::S3DVertex vertices[4] =
1699                         {
1700                                 video::S3DVertex(-BS/2,-BS/2+d,-BS/2, 0,0,0, c,
1701                                         0, 1),
1702                                 video::S3DVertex(BS/2,-BS/2+d,-BS/2, 0,0,0, c,
1703                                         1, 1),
1704                                 video::S3DVertex(BS/2,-BS/2+d,BS/2, 0,0,0, c,
1705                                         1, 0),
1706                                 video::S3DVertex(-BS/2,-BS/2+d,BS/2, 0,0,0, c,
1707                                         0, 0),
1708                         };
1709
1710                         video::SMaterial material_rail;
1711                         material_rail.setFlag(video::EMF_LIGHTING, false);
1712                         material_rail.setFlag(video::EMF_BACK_FACE_CULLING, false);
1713                         material_rail.setFlag(video::EMF_BILINEAR_FILTER, false);
1714                         material_rail.setFlag(video::EMF_FOG_ENABLE, true);
1715                         material_rail.MaterialType
1716                                         = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1717
1718                         int adjacencies = is_rail_x[0] + is_rail_x[1] + is_rail_z[0] + is_rail_z[1];
1719
1720                         // Assign textures
1721                         if(adjacencies < 2)
1722                                 material_rail.setTexture(0, g_texturesource->getTextureRaw("rail.png"));
1723                         else if(adjacencies == 2)
1724                         {
1725                                 if((is_rail_x[0] && is_rail_x[1]) || (is_rail_z[0] && is_rail_z[1]))
1726                                         material_rail.setTexture(0, g_texturesource->getTextureRaw("rail.png"));
1727                                 else
1728                                         material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_curved.png"));
1729                         }
1730                         else if(adjacencies == 3)
1731                                 material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_t_junction.png"));
1732                         else if(adjacencies == 4)
1733                                 material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_crossing.png"));
1734
1735                         // Rotate textures
1736                         int angle = 0;
1737
1738                         if(adjacencies == 1)
1739                         {
1740                                 if(is_rail_x[0] || is_rail_x[1])
1741                                         angle = 90;
1742                         }
1743                         else if(adjacencies == 2)
1744                         {
1745                                 if(is_rail_x[0] && is_rail_x[1])
1746                                         angle = 90;
1747                                 else if(is_rail_x[0] && is_rail_z[0])
1748                                         angle = 270;
1749                                 else if(is_rail_x[0] && is_rail_z[1])
1750                                         angle = 180;
1751                                 else if(is_rail_x[1] && is_rail_z[1])
1752                                         angle = 90;
1753                         }
1754                         else if(adjacencies == 3)
1755                         {
1756                                 if(!is_rail_x[0])
1757                                         angle=0;
1758                                 if(!is_rail_x[1])
1759                                         angle=180;
1760                                 if(!is_rail_z[0])
1761                                         angle=90;
1762                                 if(!is_rail_z[1])
1763                                         angle=270;
1764                         }
1765
1766                         if(angle != 0) {
1767                                 for(u16 i=0; i<4; i++)
1768                                         vertices[i].Pos.rotateXZBy(angle);
1769                         }
1770
1771                         for(s32 i=0; i<4; i++)
1772                         {
1773                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1774                         }
1775
1776                         u16 indices[] = {0,1,2,2,3,0};
1777                         collector.append(material_rail, vertices, 4, indices, 6);
1778                 }
1779
1780         }
1781
1782         /*
1783                 Add stuff from collector to mesh
1784         */
1785         
1786         scene::SMesh *mesh_new = NULL;
1787         mesh_new = new scene::SMesh();
1788         
1789         collector.fillMesh(mesh_new);
1790
1791         /*
1792                 Do some stuff to the mesh
1793         */
1794
1795         mesh_new->recalculateBoundingBox();
1796
1797         /*
1798                 Delete new mesh if it is empty
1799         */
1800
1801         if(mesh_new->getMeshBufferCount() == 0)
1802         {
1803                 mesh_new->drop();
1804                 mesh_new = NULL;
1805         }
1806
1807         if(mesh_new)
1808         {
1809 #if 0
1810                 // Usually 1-700 faces and 1-7 materials
1811                 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1812                                 <<"and uses "<<mesh_new->getMeshBufferCount()
1813                                 <<" materials (meshbuffers)"<<std::endl;
1814 #endif
1815
1816                 // Use VBO for mesh (this just would set this for ever buffer)
1817                 // This will lead to infinite memory usage because or irrlicht.
1818                 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1819
1820                 /*
1821                         NOTE: If that is enabled, some kind of a queue to the main
1822                         thread should be made which would call irrlicht to delete
1823                         the hardware buffer and then delete the mesh
1824                 */
1825         }
1826
1827         return mesh_new;
1828         
1829         //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1830 }
1831
1832 #endif // !SERVER
1833
1834 /*
1835         MapBlock
1836 */
1837
1838 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
1839                 m_parent(parent),
1840                 m_pos(pos),
1841                 changed(true),
1842                 is_underground(false),
1843                 m_lighting_expired(true),
1844                 m_day_night_differs(false),
1845                 //m_not_fully_generated(false),
1846                 m_objects(this),
1847                 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED)
1848 {
1849         data = NULL;
1850         if(dummy == false)
1851                 reallocate();
1852         
1853         //m_spawn_timer = -10000;
1854
1855 #ifndef SERVER
1856         m_mesh_expired = false;
1857         mesh_mutex.Init();
1858         mesh = NULL;
1859         m_temp_mods_mutex.Init();
1860 #endif
1861 }
1862
1863 MapBlock::~MapBlock()
1864 {
1865 #ifndef SERVER
1866         {
1867                 JMutexAutoLock lock(mesh_mutex);
1868                 
1869                 if(mesh)
1870                 {
1871                         mesh->drop();
1872                         mesh = NULL;
1873                 }
1874         }
1875 #endif
1876
1877         if(data)
1878                 delete[] data;
1879 }
1880
1881 bool MapBlock::isValidPositionParent(v3s16 p)
1882 {
1883         if(isValidPosition(p))
1884         {
1885                 return true;
1886         }
1887         else{
1888                 return m_parent->isValidPosition(getPosRelative() + p);
1889         }
1890 }
1891
1892 MapNode MapBlock::getNodeParent(v3s16 p)
1893 {
1894         if(isValidPosition(p) == false)
1895         {
1896                 return m_parent->getNode(getPosRelative() + p);
1897         }
1898         else
1899         {
1900                 if(data == NULL)
1901                         throw InvalidPositionException();
1902                 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1903         }
1904 }
1905
1906 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
1907 {
1908         if(isValidPosition(p) == false)
1909         {
1910                 m_parent->setNode(getPosRelative() + p, n);
1911         }
1912         else
1913         {
1914                 if(data == NULL)
1915                         throw InvalidPositionException();
1916                 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
1917         }
1918 }
1919
1920 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
1921 {
1922         if(isValidPosition(p) == false)
1923         {
1924                 try{
1925                         return m_parent->getNode(getPosRelative() + p);
1926                 }
1927                 catch(InvalidPositionException &e)
1928                 {
1929                         return MapNode(CONTENT_IGNORE);
1930                 }
1931         }
1932         else
1933         {
1934                 if(data == NULL)
1935                 {
1936                         return MapNode(CONTENT_IGNORE);
1937                 }
1938                 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1939         }
1940 }
1941
1942 #ifndef SERVER
1943
1944 #if 1
1945 void MapBlock::updateMesh(u32 daynight_ratio)
1946 {
1947 #if 0
1948         /*
1949                 DEBUG: If mesh has been generated, don't generate it again
1950         */
1951         {
1952                 JMutexAutoLock meshlock(mesh_mutex);
1953                 if(mesh != NULL)
1954                         return;
1955         }
1956 #endif
1957
1958         MeshMakeData data;
1959         data.fill(daynight_ratio, this);
1960         
1961         scene::SMesh *mesh_new = makeMapBlockMesh(&data);
1962         
1963         /*
1964                 Replace the mesh
1965         */
1966
1967         replaceMesh(mesh_new);
1968
1969 }
1970 #endif
1971
1972 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
1973 {
1974         mesh_mutex.Lock();
1975
1976         //scene::SMesh *mesh_old = mesh[daynight_i];
1977         //mesh[daynight_i] = mesh_new;
1978
1979         scene::SMesh *mesh_old = mesh;
1980         mesh = mesh_new;
1981         setMeshExpired(false);
1982         
1983         if(mesh_old != NULL)
1984         {
1985                 // Remove hardware buffers of meshbuffers of mesh
1986                 // NOTE: No way, this runs in a different thread and everything
1987                 /*u32 c = mesh_old->getMeshBufferCount();
1988                 for(u32 i=0; i<c; i++)
1989                 {
1990                         IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1991                 }*/
1992                 
1993                 /*dstream<<"mesh_old->getReferenceCount()="
1994                                 <<mesh_old->getReferenceCount()<<std::endl;
1995                 u32 c = mesh_old->getMeshBufferCount();
1996                 for(u32 i=0; i<c; i++)
1997                 {
1998                         scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1999                         dstream<<"buf->getReferenceCount()="
2000                                         <<buf->getReferenceCount()<<std::endl;
2001                 }*/
2002
2003                 // Drop the mesh
2004                 mesh_old->drop();
2005
2006                 //delete mesh_old;
2007         }
2008
2009         mesh_mutex.Unlock();
2010 }
2011         
2012 #endif // !SERVER
2013
2014 /*
2015         Propagates sunlight down through the block.
2016         Doesn't modify nodes that are not affected by sunlight.
2017         
2018         Returns false if sunlight at bottom block is invalid.
2019         Returns true if sunlight at bottom block is valid.
2020         Returns true if bottom block doesn't exist.
2021
2022         If there is a block above, continues from it.
2023         If there is no block above, assumes there is sunlight, unless
2024         is_underground is set or highest node is water.
2025
2026         All sunlighted nodes are added to light_sources.
2027
2028         If grow_grass==true, turns sunglighted mud into grass.
2029
2030         if remove_light==true, sets non-sunlighted nodes black.
2031
2032         if black_air_left!=NULL, it is set to true if non-sunlighted
2033         air is left in block.
2034 */
2035 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
2036                 bool remove_light, bool *black_air_left,
2037                 bool grow_grass)
2038 {
2039         // Whether the sunlight at the top of the bottom block is valid
2040         bool block_below_is_valid = true;
2041         
2042         v3s16 pos_relative = getPosRelative();
2043         
2044         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
2045         {
2046                 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
2047                 {
2048 #if 1
2049                         bool no_sunlight = false;
2050                         bool no_top_block = false;
2051                         // Check if node above block has sunlight
2052                         try{
2053                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
2054                                 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
2055                                 {
2056                                         no_sunlight = true;
2057                                 }
2058                         }
2059                         catch(InvalidPositionException &e)
2060                         {
2061                                 no_top_block = true;
2062                                 
2063                                 // NOTE: This makes over-ground roofed places sunlighted
2064                                 // Assume sunlight, unless is_underground==true
2065                                 if(is_underground)
2066                                 {
2067                                         no_sunlight = true;
2068                                 }
2069                                 else
2070                                 {
2071                                         MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
2072                                         if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
2073                                         {
2074                                                 no_sunlight = true;
2075                                         }
2076                                 }
2077                                 // NOTE: As of now, this just would make everything dark.
2078                                 // No sunlight here
2079                                 //no_sunlight = true;
2080                         }
2081 #endif
2082 #if 0 // Doesn't work; nothing gets light.
2083                         bool no_sunlight = true;
2084                         bool no_top_block = false;
2085                         // Check if node above block has sunlight
2086                         try{
2087                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
2088                                 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
2089                                 {
2090                                         no_sunlight = false;
2091                                 }
2092                         }
2093                         catch(InvalidPositionException &e)
2094                         {
2095                                 no_top_block = true;
2096                         }
2097 #endif
2098
2099                         /*std::cout<<"("<<x<<","<<z<<"): "
2100                                         <<"no_top_block="<<no_top_block
2101                                         <<", is_underground="<<is_underground
2102                                         <<", no_sunlight="<<no_sunlight
2103                                         <<std::endl;*/
2104                 
2105                         s16 y = MAP_BLOCKSIZE-1;
2106                         
2107                         // This makes difference to diminishing in water.
2108                         bool stopped_to_solid_object = false;
2109                         
2110                         u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
2111
2112                         for(; y >= 0; y--)
2113                         {
2114                                 v3s16 pos(x, y, z);
2115                                 MapNode &n = getNodeRef(pos);
2116                                 
2117                                 if(current_light == 0)
2118                                 {
2119                                         // Do nothing
2120                                 }
2121                                 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
2122                                 {
2123                                         // Do nothing: Sunlight is continued
2124                                 }
2125                                 else if(n.light_propagates() == false)
2126                                 {
2127                                         if(grow_grass)
2128                                         {
2129                                                 bool upper_is_air = false;
2130                                                 try
2131                                                 {
2132                                                         if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
2133                                                                 upper_is_air = true;
2134                                                 }
2135                                                 catch(InvalidPositionException &e)
2136                                                 {
2137                                                 }
2138                                                 // Turn mud into grass
2139                                                 if(upper_is_air && n.d == CONTENT_MUD
2140                                                                 && current_light == LIGHT_SUN)
2141                                                 {
2142                                                         n.d = CONTENT_GRASS;
2143                                                 }
2144                                         }
2145
2146                                         // A solid object is on the way.
2147                                         stopped_to_solid_object = true;
2148                                         
2149                                         // Light stops.
2150                                         current_light = 0;
2151                                 }
2152                                 else
2153                                 {
2154                                         // Diminish light
2155                                         current_light = diminish_light(current_light);
2156                                 }
2157
2158                                 u8 old_light = n.getLight(LIGHTBANK_DAY);
2159
2160                                 if(current_light > old_light || remove_light)
2161                                 {
2162                                         n.setLight(LIGHTBANK_DAY, current_light);
2163                                 }
2164                                 
2165                                 if(diminish_light(current_light) != 0)
2166                                 {
2167                                         light_sources.insert(pos_relative + pos, true);
2168                                 }
2169
2170                                 if(current_light == 0 && stopped_to_solid_object)
2171                                 {
2172                                         if(black_air_left)
2173                                         {
2174                                                 *black_air_left = true;
2175                                         }
2176                                 }
2177                         }
2178
2179                         // Whether or not the block below should see LIGHT_SUN
2180                         bool sunlight_should_go_down = (current_light == LIGHT_SUN);
2181
2182                         /*
2183                                 If the block below hasn't already been marked invalid:
2184
2185                                 Check if the node below the block has proper sunlight at top.
2186                                 If not, the block below is invalid.
2187                                 
2188                                 Ignore non-transparent nodes as they always have no light
2189                         */
2190                         try
2191                         {
2192                         if(block_below_is_valid)
2193                         {
2194                                 MapNode n = getNodeParent(v3s16(x, -1, z));
2195                                 if(n.light_propagates())
2196                                 {
2197                                         if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
2198                                                         && sunlight_should_go_down == false)
2199                                                 block_below_is_valid = false;
2200                                         else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
2201                                                         && sunlight_should_go_down == true)
2202                                                 block_below_is_valid = false;
2203                                 }
2204                         }//if
2205                         }//try
2206                         catch(InvalidPositionException &e)
2207                         {
2208                                 /*std::cout<<"InvalidBlockException for bottom block node"
2209                                                 <<std::endl;*/
2210                                 // Just no block below, no need to panic.
2211                         }
2212                 }
2213         }
2214
2215         return block_below_is_valid;
2216 }
2217
2218
2219 void MapBlock::copyTo(VoxelManipulator &dst)
2220 {
2221         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
2222         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
2223         
2224         // Copy from data to VoxelManipulator
2225         dst.copyFrom(data, data_area, v3s16(0,0,0),
2226                         getPosRelative(), data_size);
2227 }
2228
2229 void MapBlock::copyFrom(VoxelManipulator &dst)
2230 {
2231         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
2232         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
2233         
2234         // Copy from VoxelManipulator to data
2235         dst.copyTo(data, data_area, v3s16(0,0,0),
2236                         getPosRelative(), data_size);
2237 }
2238
2239 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
2240 {
2241         /*
2242                 Step objects
2243         */
2244         m_objects.step(dtime, server, daynight_ratio);
2245
2246         setChangedFlag();
2247 }
2248
2249
2250 void MapBlock::updateDayNightDiff()
2251 {
2252         if(data == NULL)
2253         {
2254                 m_day_night_differs = false;
2255                 return;
2256         }
2257
2258         bool differs = false;
2259
2260         /*
2261                 Check if any lighting value differs
2262         */
2263         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2264         {
2265                 MapNode &n = data[i];
2266                 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
2267                 {
2268                         differs = true;
2269                         break;
2270                 }
2271         }
2272
2273         /*
2274                 If some lighting values differ, check if the whole thing is
2275                 just air. If it is, differ = false
2276         */
2277         if(differs)
2278         {
2279                 bool only_air = true;
2280                 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2281                 {
2282                         MapNode &n = data[i];
2283                         if(n.d != CONTENT_AIR)
2284                         {
2285                                 only_air = false;
2286                                 break;
2287                         }
2288                 }
2289                 if(only_air)
2290                         differs = false;
2291         }
2292
2293         // Set member variable
2294         m_day_night_differs = differs;
2295 }
2296
2297 s16 MapBlock::getGroundLevel(v2s16 p2d)
2298 {
2299         if(isDummy())
2300                 return -3;
2301         try
2302         {
2303                 s16 y = MAP_BLOCKSIZE-1;
2304                 for(; y>=0; y--)
2305                 {
2306                         //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
2307                         if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
2308                         {
2309                                 if(y == MAP_BLOCKSIZE-1)
2310                                         return -2;
2311                                 else
2312                                         return y;
2313                         }
2314                 }
2315                 return -1;
2316         }
2317         catch(InvalidPositionException &e)
2318         {
2319                 return -3;
2320         }
2321 }
2322
2323 /*
2324         Serialization
2325 */
2326
2327 void MapBlock::serialize(std::ostream &os, u8 version)
2328 {
2329         if(!ser_ver_supported(version))
2330                 throw VersionMismatchException("ERROR: MapBlock format not supported");
2331         
2332         if(data == NULL)
2333         {
2334                 throw SerializationError("ERROR: Not writing dummy block.");
2335         }
2336         
2337         // These have no compression
2338         if(version <= 3 || version == 5 || version == 6)
2339         {
2340                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2341                 
2342                 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
2343                 SharedBuffer<u8> dest(buflen);
2344
2345                 dest[0] = is_underground;
2346                 for(u32 i=0; i<nodecount; i++)
2347                 {
2348                         u32 s = 1 + i * MapNode::serializedLength(version);
2349                         data[i].serialize(&dest[s], version);
2350                 }
2351                 
2352                 os.write((char*)*dest, dest.getSize());
2353         }
2354         else if(version <= 10)
2355         {
2356                 /*
2357                         With compression.
2358                         Compress the materials and the params separately.
2359                 */
2360                 
2361                 // First byte
2362                 os.write((char*)&is_underground, 1);
2363
2364                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2365
2366                 // Get and compress materials
2367                 SharedBuffer<u8> materialdata(nodecount);
2368                 for(u32 i=0; i<nodecount; i++)
2369                 {
2370                         materialdata[i] = data[i].d;
2371                 }
2372                 compress(materialdata, os, version);
2373
2374                 // Get and compress lights
2375                 SharedBuffer<u8> lightdata(nodecount);
2376                 for(u32 i=0; i<nodecount; i++)
2377                 {
2378                         lightdata[i] = data[i].param;
2379                 }
2380                 compress(lightdata, os, version);
2381                 
2382                 if(version >= 10)
2383                 {
2384                         // Get and compress param2
2385                         SharedBuffer<u8> param2data(nodecount);
2386                         for(u32 i=0; i<nodecount; i++)
2387                         {
2388                                 param2data[i] = data[i].param2;
2389                         }
2390                         compress(param2data, os, version);
2391                 }
2392         }
2393         // All other versions (newest)
2394         else
2395         {
2396                 // First byte
2397                 u8 flags = 0;
2398                 if(is_underground)
2399                         flags |= 0x01;
2400                 if(m_day_night_differs)
2401                         flags |= 0x02;
2402                 if(m_lighting_expired)
2403                         flags |= 0x04;
2404                 os.write((char*)&flags, 1);
2405
2406                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2407
2408                 /*
2409                         Get data
2410                 */
2411
2412                 SharedBuffer<u8> databuf(nodecount*3);
2413
2414                 // Get contents
2415                 for(u32 i=0; i<nodecount; i++)
2416                 {
2417                         databuf[i] = data[i].d;
2418                 }
2419
2420                 // Get params
2421                 for(u32 i=0; i<nodecount; i++)
2422                 {
2423                         databuf[i+nodecount] = data[i].param;
2424                 }
2425
2426                 // Get param2
2427                 for(u32 i=0; i<nodecount; i++)
2428                 {
2429                         databuf[i+nodecount*2] = data[i].param2;
2430                 }
2431
2432                 /*
2433                         Compress data to output stream
2434                 */
2435
2436                 compress(databuf, os, version);
2437                 
2438                 /*
2439                         NodeMetadata
2440                 */
2441                 if(version >= 14)
2442                 {
2443                         if(version <= 15)
2444                         {
2445                                 try{
2446                                         std::ostringstream oss(std::ios_base::binary);
2447                                         m_node_metadata.serialize(oss);
2448                                         os<<serializeString(oss.str());
2449                                 }
2450                                 // This will happen if the string is longer than 65535
2451                                 catch(SerializationError &e)
2452                                 {
2453                                         // Use an empty string
2454                                         os<<serializeString("");
2455                                 }
2456                         }
2457                         else
2458                         {
2459                                 std::ostringstream oss(std::ios_base::binary);
2460                                 m_node_metadata.serialize(oss);
2461                                 compressZlib(oss.str(), os);
2462                                 //os<<serializeLongString(oss.str());
2463                         }
2464                 }
2465         }
2466 }
2467
2468 void MapBlock::deSerialize(std::istream &is, u8 version)
2469 {
2470         if(!ser_ver_supported(version))
2471                 throw VersionMismatchException("ERROR: MapBlock format not supported");
2472
2473         // These have no lighting info
2474         if(version <= 1)
2475         {
2476                 setLightingExpired(true);
2477         }
2478
2479         // These have no compression
2480         if(version <= 3 || version == 5 || version == 6)
2481         {
2482                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2483                 char tmp;
2484                 is.read(&tmp, 1);
2485                 if(is.gcount() != 1)
2486                         throw SerializationError
2487                                         ("MapBlock::deSerialize: no enough input data");
2488                 is_underground = tmp;
2489                 for(u32 i=0; i<nodecount; i++)
2490                 {
2491                         s32 len = MapNode::serializedLength(version);
2492                         SharedBuffer<u8> d(len);
2493                         is.read((char*)*d, len);
2494                         if(is.gcount() != len)
2495                                 throw SerializationError
2496                                                 ("MapBlock::deSerialize: no enough input data");
2497                         data[i].deSerialize(*d, version);
2498                 }
2499         }
2500         else if(version <= 10)
2501         {
2502                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2503
2504                 u8 t8;
2505                 is.read((char*)&t8, 1);
2506                 is_underground = t8;
2507
2508                 {
2509                         // Uncompress and set material data
2510                         std::ostringstream os(std::ios_base::binary);
2511                         decompress(is, os, version);
2512                         std::string s = os.str();
2513                         if(s.size() != nodecount)
2514                                 throw SerializationError
2515                                                 ("MapBlock::deSerialize: invalid format");
2516                         for(u32 i=0; i<s.size(); i++)
2517                         {
2518                                 data[i].d = s[i];
2519                         }
2520                 }
2521                 {
2522                         // Uncompress and set param data
2523                         std::ostringstream os(std::ios_base::binary);
2524                         decompress(is, os, version);
2525                         std::string s = os.str();
2526                         if(s.size() != nodecount)
2527                                 throw SerializationError
2528                                                 ("MapBlock::deSerialize: invalid format");
2529                         for(u32 i=0; i<s.size(); i++)
2530                         {
2531                                 data[i].param = s[i];
2532                         }
2533                 }
2534         
2535                 if(version >= 10)
2536                 {
2537                         // Uncompress and set param2 data
2538                         std::ostringstream os(std::ios_base::binary);
2539                         decompress(is, os, version);
2540                         std::string s = os.str();
2541                         if(s.size() != nodecount)
2542                                 throw SerializationError
2543                                                 ("MapBlock::deSerialize: invalid format");
2544                         for(u32 i=0; i<s.size(); i++)
2545                         {
2546                                 data[i].param2 = s[i];
2547                         }
2548                 }
2549         }
2550         // All other versions (newest)
2551         else
2552         {
2553                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2554
2555                 u8 flags;
2556                 is.read((char*)&flags, 1);
2557                 is_underground = (flags & 0x01) ? true : false;
2558                 m_day_night_differs = (flags & 0x02) ? true : false;
2559                 m_lighting_expired = (flags & 0x04) ? true : false;
2560
2561                 // Uncompress data
2562                 std::ostringstream os(std::ios_base::binary);
2563                 decompress(is, os, version);
2564                 std::string s = os.str();
2565                 if(s.size() != nodecount*3)
2566                         throw SerializationError
2567                                         ("MapBlock::deSerialize: invalid format");
2568
2569                 // Set contents
2570                 for(u32 i=0; i<nodecount; i++)
2571                 {
2572                         data[i].d = s[i];
2573                 }
2574                 // Set params
2575                 for(u32 i=0; i<nodecount; i++)
2576                 {
2577                         data[i].param = s[i+nodecount];
2578                 }
2579                 // Set param2
2580                 for(u32 i=0; i<nodecount; i++)
2581                 {
2582                         data[i].param2 = s[i+nodecount*2];
2583                 }
2584                 
2585                 /*
2586                         NodeMetadata
2587                 */
2588                 if(version >= 14)
2589                 {
2590                         // Ignore errors
2591                         try{
2592                                 if(version <= 15)
2593                                 {
2594                                         std::string data = deSerializeString(is);
2595                                         std::istringstream iss(data, std::ios_base::binary);
2596                                         m_node_metadata.deSerialize(iss);
2597                                 }
2598                                 else
2599                                 {
2600                                         //std::string data = deSerializeLongString(is);
2601                                         std::ostringstream oss(std::ios_base::binary);
2602                                         decompressZlib(is, oss);
2603                                         std::istringstream iss(oss.str(), std::ios_base::binary);
2604                                         m_node_metadata.deSerialize(iss);
2605                                 }
2606                         }
2607                         catch(SerializationError &e)
2608                         {
2609                                 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
2610                                                 <<" while deserializing node metadata"<<std::endl;
2611                         }
2612                 }
2613         }
2614         
2615         /*
2616                 Translate nodes as specified in the translate_to fields of
2617                 node features
2618
2619                 NOTE: This isn't really used. Should it be removed?
2620         */
2621         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2622         {
2623                 MapNode &n = data[i];
2624
2625                 MapNode *translate_to = content_features(n.d).translate_to;
2626                 if(translate_to)
2627                 {
2628                         dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
2629                                         <<translate_to->d<<std::endl;
2630                         n = *translate_to;
2631                 }
2632         }
2633 }
2634
2635 void MapBlock::serializeDiskExtra(std::ostream &os, u8 version)
2636 {
2637         // Versions up from 9 have block objects.
2638         if(version >= 9)
2639         {
2640                 serializeObjects(os, version);
2641         }
2642         
2643         // Versions up from 15 have static objects.
2644         if(version >= 15)
2645         {
2646                 m_static_objects.serialize(os);
2647         }
2648
2649         // Timestamp
2650         if(version >= 17)
2651         {
2652                 writeU32(os, getTimestamp());
2653         }
2654 }
2655
2656 void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
2657 {
2658         /*
2659                 Versions up from 9 have block objects.
2660         */
2661         if(version >= 9)
2662         {
2663                 updateObjects(is, version, NULL, 0);
2664         }
2665
2666         /*
2667                 Versions up from 15 have static objects.
2668         */
2669         if(version >= 15)
2670         {
2671                 m_static_objects.deSerialize(is);
2672         }
2673                 
2674         // Timestamp
2675         if(version >= 17)
2676         {
2677                 setTimestamp(readU32(is));
2678         }
2679         else
2680         {
2681                 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
2682         }
2683 }
2684
2685
2686 //END