]> git.lizzy.rs Git - dragonfireclient.git/blob - src/mapblock.cpp
...Now the tesselation should actually work
[dragonfireclient.git] / src / mapblock.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "mapblock.h"
21 #include "map.h"
22 // For g_settings
23 #include "main.h"
24 #include "light.h"
25 #include <sstream>
26
27 #ifndef SERVER
28 void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block)
29 {
30         m_daynight_ratio = daynight_ratio;
31         m_blockpos = block->getPos();
32
33         v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
34         
35         /*
36                 There is no harm not copying the TempMods of the neighbors
37                 because they are already copied to this block
38         */
39         m_temp_mods.clear();
40         block->copyTempMods(m_temp_mods);
41         
42         /*
43                 Copy data
44         */
45
46         // Allocate this block + neighbors
47         m_vmanip.clear();
48         m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
49                         blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
50
51         {
52                 //TimeTaker timer("copy central block data");
53                 // 0ms
54
55                 // Copy our data
56                 block->copyTo(m_vmanip);
57         }
58         {
59                 //TimeTaker timer("copy neighbor block data");
60                 // 0ms
61
62                 /*
63                         Copy neighbors. This is lightning fast.
64                         Copying only the borders would be *very* slow.
65                 */
66                 
67                 // Get map
68                 NodeContainer *parentcontainer = block->getParent();
69                 // This will only work if the parent is the map
70                 assert(parentcontainer->nodeContainerId() == NODECONTAINER_ID_MAP);
71                 // OK, we have the map!
72                 Map *map = (Map*)parentcontainer;
73
74                 for(u16 i=0; i<6; i++)
75                 {
76                         const v3s16 &dir = g_6dirs[i];
77                         v3s16 bp = m_blockpos + dir;
78                         MapBlock *b = map->getBlockNoCreateNoEx(bp);
79                         if(b)
80                                 b->copyTo(m_vmanip);
81                 }
82         }
83 }
84 #endif
85
86 /*
87         Parameters must consist of air and !air.
88         Order doesn't matter.
89
90         If either of the nodes doesn't exist, light is 0.
91         
92         parameters:
93                 daynight_ratio: 0...1000
94                 n: getNodeParent(p)
95                 n2: getNodeParent(p + face_dir)
96                 face_dir: axis oriented unit vector from p to p2
97         
98         returns encoded light value.
99 */
100 u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
101                 v3s16 face_dir)
102 {
103         try{
104                 u8 light;
105                 u8 l1 = n.getLightBlend(daynight_ratio);
106                 u8 l2 = n2.getLightBlend(daynight_ratio);
107                 if(l1 > l2)
108                         light = l1;
109                 else
110                         light = l2;
111
112                 // Make some nice difference to different sides
113
114                 // This makes light come from a corner
115                 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
116                         light = diminish_light(diminish_light(light));
117                 else if(face_dir.X == -1 || face_dir.Z == -1)
118                         light = diminish_light(light);*/
119                 
120                 // All neighboring faces have different shade (like in minecraft)
121                 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
122                         light = diminish_light(diminish_light(light));
123                 else if(face_dir.Z == 1 || face_dir.Z == -1)
124                         light = diminish_light(light);
125
126                 return light;
127         }
128         catch(InvalidPositionException &e)
129         {
130                 return 0;
131         }
132 }
133
134 #ifndef SERVER
135
136 /*
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                         #if 0
623                                 // First node
624                                 v3s16 p_first = p_corrected - (continuous_tiles_count-1)
625                                                 * translate_dir;
626
627                                 v3s16 p_map_leftmost;
628                                 v3s16 p_map_rightmost;
629                                 p_map_leftmost = p_corrected + blockpos_nodes;
630                                 p_map_rightmost = p_first + blockpos_nodes;
631                                 
632                                 /*if(p != p_corrected)
633                                 {
634                                         if(face_dir == v3s16(0,0,1))
635                                         {
636                                                 v3s16 orig_leftmost = p_map_leftmost;
637                                                 v3s16 orig_rightmost = p_map_leftmost;
638                                                 p_map_leftmost = orig_rightmost;
639                                                 p_map_rightmost = orig_leftmost;
640                                         }
641                                 }*/
642
643                                 if(smooth_lighting == false)
644                                 {
645                                         li0 = li1 = li2 = li3 = decode_light(light);
646                                 }
647                                 else
648                                 {
649                                         v3s16 vertex_dirs[4];
650                                         getNodeVertexDirs(face_dir_corrected, vertex_dirs);
651                                         
652                                         li0 = getSmoothLight(p_map_rightmost, vertex_dirs[0],
653                                                         vmanip, daynight_ratio);
654                                         li1 = getSmoothLight(p_map_leftmost, vertex_dirs[1],
655                                                         vmanip, daynight_ratio);
656                                         li2 = getSmoothLight(p_map_leftmost, vertex_dirs[2],
657                                                         vmanip, daynight_ratio);
658                                         li3 = getSmoothLight(p_map_rightmost, vertex_dirs[3],
659                                                         vmanip, daynight_ratio);
660                                 }
661                                 makeFastFace(tile, li0, li1, li2, li3,
662                                                 sp, face_dir_corrected, scale,
663                                                 posRelative_f, dest);
664                         #endif
665                         }
666
667                         continuous_tiles_count = 0;
668                         
669                         makes_face = next_makes_face;
670                         p_corrected = next_p_corrected;
671                         face_dir_corrected = next_face_dir_corrected;
672                         lights[0] = next_lights[0];
673                         lights[1] = next_lights[1];
674                         lights[2] = next_lights[2];
675                         lights[3] = next_lights[3];
676                         tile = next_tile;
677                 }
678                 
679                 p = p_next;
680         }
681 }
682
683 /*
684         This is used because CMeshBuffer::append() is very slow
685 */
686 struct PreMeshBuffer
687 {
688         video::SMaterial material;
689         core::array<u16> indices;
690         core::array<video::S3DVertex> vertices;
691 };
692
693 class MeshCollector
694 {
695 public:
696         void append(
697                         video::SMaterial material,
698                         const video::S3DVertex* const vertices,
699                         u32 numVertices,
700                         const u16* const indices,
701                         u32 numIndices
702                 )
703         {
704                 PreMeshBuffer *p = NULL;
705                 for(u32 i=0; i<m_prebuffers.size(); i++)
706                 {
707                         PreMeshBuffer &pp = m_prebuffers[i];
708                         if(pp.material != material)
709                                 continue;
710
711                         p = &pp;
712                         break;
713                 }
714
715                 if(p == NULL)
716                 {
717                         PreMeshBuffer pp;
718                         pp.material = material;
719                         m_prebuffers.push_back(pp);
720                         p = &m_prebuffers[m_prebuffers.size()-1];
721                 }
722
723                 u32 vertex_count = p->vertices.size();
724                 for(u32 i=0; i<numIndices; i++)
725                 {
726                         u32 j = indices[i] + vertex_count;
727                         if(j > 65535)
728                         {
729                                 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
730                                 // NOTE: Fix is to just add an another MeshBuffer
731                         }
732                         p->indices.push_back(j);
733                 }
734                 for(u32 i=0; i<numVertices; i++)
735                 {
736                         p->vertices.push_back(vertices[i]);
737                 }
738         }
739
740         void fillMesh(scene::SMesh *mesh)
741         {
742                 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
743                                 <<" meshbuffers"<<std::endl;*/
744                 for(u32 i=0; i<m_prebuffers.size(); i++)
745                 {
746                         PreMeshBuffer &p = m_prebuffers[i];
747
748                         /*dstream<<"p.vertices.size()="<<p.vertices.size()
749                                         <<", p.indices.size()="<<p.indices.size()
750                                         <<std::endl;*/
751                         
752                         // Create meshbuffer
753                         
754                         // This is a "Standard MeshBuffer",
755                         // it's a typedeffed CMeshBuffer<video::S3DVertex>
756                         scene::SMeshBuffer *buf = new scene::SMeshBuffer();
757                         // Set material
758                         buf->Material = p.material;
759                         //((scene::SMeshBuffer*)buf)->Material = p.material;
760                         // Use VBO
761                         //buf->setHardwareMappingHint(scene::EHM_STATIC);
762                         // Add to mesh
763                         mesh->addMeshBuffer(buf);
764                         // Mesh grabbed it
765                         buf->drop();
766
767                         buf->append(p.vertices.pointer(), p.vertices.size(),
768                                         p.indices.pointer(), p.indices.size());
769                 }
770         }
771
772 private:
773         core::array<PreMeshBuffer> m_prebuffers;
774 };
775
776 scene::SMesh* makeMapBlockMesh(MeshMakeData *data)
777 {
778         // 4-21ms for MAP_BLOCKSIZE=16
779         // 24-155ms for MAP_BLOCKSIZE=32
780         //TimeTaker timer1("makeMapBlockMesh()");
781
782         core::array<FastFace> fastfaces_new;
783
784         v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
785         
786         // floating point conversion
787         v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
788         
789         /*
790                 Some settings
791         */
792         bool new_style_water = g_settings.getBool("new_style_water");
793         bool new_style_leaves = g_settings.getBool("new_style_leaves");
794         bool smooth_lighting = g_settings.getBool("smooth_lighting");
795         
796         float node_water_level = 1.0;
797         if(new_style_water)
798                 node_water_level = 0.85;
799         
800         /*
801                 We are including the faces of the trailing edges of the block.
802                 This means that when something changes, the caller must
803                 also update the meshes of the blocks at the leading edges.
804
805                 NOTE: This is the slowest part of this method.
806         */
807         
808         {
809                 // 4-23ms for MAP_BLOCKSIZE=16
810                 //TimeTaker timer2("updateMesh() collect");
811
812                 /*
813                         Go through every y,z and get top(y+) faces in rows of x+
814                 */
815                 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
816                         for(s16 z=0; z<MAP_BLOCKSIZE; z++){
817                                 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
818                                                 v3s16(0,y,z), MAP_BLOCKSIZE,
819                                                 v3s16(1,0,0), //dir
820                                                 v3f  (1,0,0),
821                                                 v3s16(0,1,0), //face dir
822                                                 v3f  (0,1,0),
823                                                 fastfaces_new,
824                                                 data->m_temp_mods,
825                                                 data->m_vmanip,
826                                                 blockpos_nodes,
827                                                 smooth_lighting);
828                         }
829                 }
830                 /*
831                         Go through every x,y and get right(x+) faces in rows of z+
832                 */
833                 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
834                         for(s16 y=0; y<MAP_BLOCKSIZE; y++){
835                                 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
836                                                 v3s16(x,y,0), MAP_BLOCKSIZE,
837                                                 v3s16(0,0,1),
838                                                 v3f  (0,0,1),
839                                                 v3s16(1,0,0),
840                                                 v3f  (1,0,0),
841                                                 fastfaces_new,
842                                                 data->m_temp_mods,
843                                                 data->m_vmanip,
844                                                 blockpos_nodes,
845                                                 smooth_lighting);
846                         }
847                 }
848                 /*
849                         Go through every y,z and get back(z+) faces in rows of x+
850                 */
851                 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
852                         for(s16 y=0; y<MAP_BLOCKSIZE; y++){
853                                 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
854                                                 v3s16(0,y,z), MAP_BLOCKSIZE,
855                                                 v3s16(1,0,0),
856                                                 v3f  (1,0,0),
857                                                 v3s16(0,0,1),
858                                                 v3f  (0,0,1),
859                                                 fastfaces_new,
860                                                 data->m_temp_mods,
861                                                 data->m_vmanip,
862                                                 blockpos_nodes,
863                                                 smooth_lighting);
864                         }
865                 }
866         }
867
868         // End of slow part
869
870         /*
871                 Convert FastFaces to SMesh
872         */
873
874         MeshCollector collector;
875
876         if(fastfaces_new.size() > 0)
877         {
878                 // avg 0ms (100ms spikes when loading textures the first time)
879                 //TimeTaker timer2("updateMesh() mesh building");
880
881                 video::SMaterial material;
882                 material.setFlag(video::EMF_LIGHTING, false);
883                 material.setFlag(video::EMF_BILINEAR_FILTER, false);
884                 material.setFlag(video::EMF_FOG_ENABLE, true);
885                 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
886                 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
887
888                 for(u32 i=0; i<fastfaces_new.size(); i++)
889                 {
890                         FastFace &f = fastfaces_new[i];
891
892                         const u16 indices[] = {0,1,2,2,3,0};
893                         
894                         video::ITexture *texture = f.tile.texture.atlas;
895                         if(texture == NULL)
896                                 continue;
897
898                         material.setTexture(0, texture);
899                         
900                         f.tile.applyMaterialOptions(material);
901                         
902                         collector.append(material, f.vertices, 4, indices, 6);
903                 }
904         }
905
906         /*
907                 Add special graphics:
908                 - torches
909                 - flowing water
910         */
911
912         // 0ms
913         //TimeTaker timer2("updateMesh() adding special stuff");
914
915         // Flowing water material
916         video::SMaterial material_water1;
917         material_water1.setFlag(video::EMF_LIGHTING, false);
918         material_water1.setFlag(video::EMF_BACK_FACE_CULLING, false);
919         material_water1.setFlag(video::EMF_BILINEAR_FILTER, false);
920         material_water1.setFlag(video::EMF_FOG_ENABLE, true);
921         material_water1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
922         AtlasPointer pa_water1 = g_texturesource->getTexture(
923                         g_texturesource->getTextureId("water.png"));
924         material_water1.setTexture(0, pa_water1.atlas);
925
926         // New-style leaves material
927         video::SMaterial material_leaves1;
928         material_leaves1.setFlag(video::EMF_LIGHTING, false);
929         //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
930         material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
931         material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
932         material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
933         AtlasPointer pa_leaves1 = g_texturesource->getTexture(
934                         g_texturesource->getTextureId("leaves.png"));
935         material_leaves1.setTexture(0, pa_leaves1.atlas);
936
937         for(s16 z=0; z<MAP_BLOCKSIZE; z++)
938         for(s16 y=0; y<MAP_BLOCKSIZE; y++)
939         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
940         {
941                 v3s16 p(x,y,z);
942
943                 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p);
944                 
945                 /*
946                         Add torches to mesh
947                 */
948                 if(n.d == CONTENT_TORCH)
949                 {
950                         video::SColor c(255,255,255,255);
951
952                         // Wall at X+ of node
953                         video::S3DVertex vertices[4] =
954                         {
955                                 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
956                                 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
957                                 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
958                                 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
959                         };
960
961                         v3s16 dir = unpackDir(n.dir);
962
963                         for(s32 i=0; i<4; i++)
964                         {
965                                 if(dir == v3s16(1,0,0))
966                                         vertices[i].Pos.rotateXZBy(0);
967                                 if(dir == v3s16(-1,0,0))
968                                         vertices[i].Pos.rotateXZBy(180);
969                                 if(dir == v3s16(0,0,1))
970                                         vertices[i].Pos.rotateXZBy(90);
971                                 if(dir == v3s16(0,0,-1))
972                                         vertices[i].Pos.rotateXZBy(-90);
973                                 if(dir == v3s16(0,-1,0))
974                                         vertices[i].Pos.rotateXZBy(45);
975                                 if(dir == v3s16(0,1,0))
976                                         vertices[i].Pos.rotateXZBy(-45);
977
978                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
979                         }
980
981                         // Set material
982                         video::SMaterial material;
983                         material.setFlag(video::EMF_LIGHTING, false);
984                         material.setFlag(video::EMF_BACK_FACE_CULLING, false);
985                         material.setFlag(video::EMF_BILINEAR_FILTER, false);
986                         //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
987                         material.MaterialType
988                                         = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
989
990                         if(dir == v3s16(0,-1,0))
991                                 material.setTexture(0,
992                                                 g_texturesource->getTextureRaw("torch_on_floor.png"));
993                         else if(dir == v3s16(0,1,0))
994                                 material.setTexture(0,
995                                                 g_texturesource->getTextureRaw("torch_on_ceiling.png"));
996                         // For backwards compatibility
997                         else if(dir == v3s16(0,0,0))
998                                 material.setTexture(0,
999                                                 g_texturesource->getTextureRaw("torch_on_floor.png"));
1000                         else
1001                                 material.setTexture(0, 
1002                                                 g_texturesource->getTextureRaw("torch.png"));
1003
1004                         u16 indices[] = {0,1,2,2,3,0};
1005                         // Add to mesh collector
1006                         collector.append(material, vertices, 4, indices, 6);
1007                 }
1008                 /*
1009                         Signs on walls
1010                 */
1011                 if(n.d == CONTENT_SIGN_WALL)
1012                 {
1013                         u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1014                         video::SColor c(255,l,l,l);
1015                                 
1016                         float d = (float)BS/16;
1017                         // Wall at X+ of node
1018                         video::S3DVertex vertices[4] =
1019                         {
1020                                 video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1),
1021                                 video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1),
1022                                 video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0),
1023                                 video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
1024                         };
1025
1026                         v3s16 dir = unpackDir(n.dir);
1027
1028                         for(s32 i=0; i<4; i++)
1029                         {
1030                                 if(dir == v3s16(1,0,0))
1031                                         vertices[i].Pos.rotateXZBy(0);
1032                                 if(dir == v3s16(-1,0,0))
1033                                         vertices[i].Pos.rotateXZBy(180);
1034                                 if(dir == v3s16(0,0,1))
1035                                         vertices[i].Pos.rotateXZBy(90);
1036                                 if(dir == v3s16(0,0,-1))
1037                                         vertices[i].Pos.rotateXZBy(-90);
1038                                 if(dir == v3s16(0,-1,0))
1039                                         vertices[i].Pos.rotateXYBy(-90);
1040                                 if(dir == v3s16(0,1,0))
1041                                         vertices[i].Pos.rotateXYBy(90);
1042
1043                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1044                         }
1045
1046                         // Set material
1047                         video::SMaterial material;
1048                         material.setFlag(video::EMF_LIGHTING, false);
1049                         material.setFlag(video::EMF_BACK_FACE_CULLING, false);
1050                         material.setFlag(video::EMF_BILINEAR_FILTER, false);
1051                         material.setFlag(video::EMF_FOG_ENABLE, true);
1052                         //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
1053                         material.MaterialType
1054                                         = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1055
1056                         material.setTexture(0, 
1057                                         g_texturesource->getTextureRaw("sign_wall.png"));
1058
1059                         u16 indices[] = {0,1,2,2,3,0};
1060                         // Add to mesh collector
1061                         collector.append(material, vertices, 4, indices, 6);
1062                 }
1063                 /*
1064                         Add flowing water to mesh
1065                 */
1066                 else if(n.d == CONTENT_WATER)
1067                 {
1068                         bool top_is_water = false;
1069                         MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1070                         if(ntop.d == CONTENT_WATER || ntop.d == CONTENT_WATERSOURCE)
1071                                 top_is_water = true;
1072                         
1073                         u8 l = 0;
1074                         // Use the light of the node on top if possible
1075                         if(content_features(ntop.d).param_type == CPT_LIGHT)
1076                                 l = decode_light(ntop.getLightBlend(data->m_daynight_ratio));
1077                         // Otherwise use the light of this node (the water)
1078                         else
1079                                 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1080                         video::SColor c(WATER_ALPHA,l,l,l);
1081                         
1082                         // Neighbor water levels (key = relative position)
1083                         // Includes current node
1084                         core::map<v3s16, f32> neighbor_levels;
1085                         core::map<v3s16, u8> neighbor_contents;
1086                         core::map<v3s16, u8> neighbor_flags;
1087                         const u8 neighborflag_top_is_water = 0x01;
1088                         v3s16 neighbor_dirs[9] = {
1089                                 v3s16(0,0,0),
1090                                 v3s16(0,0,1),
1091                                 v3s16(0,0,-1),
1092                                 v3s16(1,0,0),
1093                                 v3s16(-1,0,0),
1094                                 v3s16(1,0,1),
1095                                 v3s16(-1,0,-1),
1096                                 v3s16(1,0,-1),
1097                                 v3s16(-1,0,1),
1098                         };
1099                         for(u32 i=0; i<9; i++)
1100                         {
1101                                 u8 content = CONTENT_AIR;
1102                                 float level = -0.5 * BS;
1103                                 u8 flags = 0;
1104                                 // Check neighbor
1105                                 v3s16 p2 = p + neighbor_dirs[i];
1106                                 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1107                                 if(n2.d != CONTENT_IGNORE)
1108                                 {
1109                                         content = n2.d;
1110
1111                                         if(n2.d == CONTENT_WATERSOURCE)
1112                                                 level = (-0.5+node_water_level) * BS;
1113                                         else if(n2.d == CONTENT_WATER)
1114                                                 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0
1115                                                                 * node_water_level) * BS;
1116
1117                                         // Check node above neighbor.
1118                                         // NOTE: This doesn't get executed if neighbor
1119                                         //       doesn't exist
1120                                         p2.Y += 1;
1121                                         n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1122                                         if(n2.d == CONTENT_WATERSOURCE || n2.d == CONTENT_WATER)
1123                                                 flags |= neighborflag_top_is_water;
1124                                 }
1125                                 
1126                                 neighbor_levels.insert(neighbor_dirs[i], level);
1127                                 neighbor_contents.insert(neighbor_dirs[i], content);
1128                                 neighbor_flags.insert(neighbor_dirs[i], flags);
1129                         }
1130
1131                         //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
1132                         //float water_level = neighbor_levels[v3s16(0,0,0)];
1133
1134                         // Corner heights (average between four waters)
1135                         f32 corner_levels[4];
1136                         
1137                         v3s16 halfdirs[4] = {
1138                                 v3s16(0,0,0),
1139                                 v3s16(1,0,0),
1140                                 v3s16(1,0,1),
1141                                 v3s16(0,0,1),
1142                         };
1143                         for(u32 i=0; i<4; i++)
1144                         {
1145                                 v3s16 cornerdir = halfdirs[i];
1146                                 float cornerlevel = 0;
1147                                 u32 valid_count = 0;
1148                                 for(u32 j=0; j<4; j++)
1149                                 {
1150                                         v3s16 neighbordir = cornerdir - halfdirs[j];
1151                                         u8 content = neighbor_contents[neighbordir];
1152                                         // Special case for source nodes
1153                                         if(content == CONTENT_WATERSOURCE)
1154                                         {
1155                                                 cornerlevel = (-0.5+node_water_level)*BS;
1156                                                 valid_count = 1;
1157                                                 break;
1158                                         }
1159                                         else if(content == CONTENT_WATER)
1160                                         {
1161                                                 cornerlevel += neighbor_levels[neighbordir];
1162                                                 valid_count++;
1163                                         }
1164                                         else if(content == CONTENT_AIR)
1165                                         {
1166                                                 cornerlevel += -0.5*BS;
1167                                                 valid_count++;
1168                                         }
1169                                 }
1170                                 if(valid_count > 0)
1171                                         cornerlevel /= valid_count;
1172                                 corner_levels[i] = cornerlevel;
1173                         }
1174
1175                         /*
1176                                 Generate sides
1177                         */
1178
1179                         v3s16 side_dirs[4] = {
1180                                 v3s16(1,0,0),
1181                                 v3s16(-1,0,0),
1182                                 v3s16(0,0,1),
1183                                 v3s16(0,0,-1),
1184                         };
1185                         s16 side_corners[4][2] = {
1186                                 {1, 2},
1187                                 {3, 0},
1188                                 {2, 3},
1189                                 {0, 1},
1190                         };
1191                         for(u32 i=0; i<4; i++)
1192                         {
1193                                 v3s16 dir = side_dirs[i];
1194
1195                                 /*
1196                                         If our topside is water and neighbor's topside
1197                                         is water, don't draw side face
1198                                 */
1199                                 if(top_is_water &&
1200                                                 neighbor_flags[dir] & neighborflag_top_is_water)
1201                                         continue;
1202
1203                                 u8 neighbor_content = neighbor_contents[dir];
1204                                 
1205                                 // Don't draw face if neighbor is not air or water
1206                                 if(neighbor_content != CONTENT_AIR
1207                                                 && neighbor_content != CONTENT_WATER)
1208                                         continue;
1209                                 
1210                                 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
1211                                 
1212                                 // Don't draw any faces if neighbor is water and top is water
1213                                 if(neighbor_is_water == true && top_is_water == false)
1214                                         continue;
1215                                 
1216                                 video::S3DVertex vertices[4] =
1217                                 {
1218                                         /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
1219                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
1220                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1221                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1222                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1223                                                         pa_water1.x0(), pa_water1.y1()),
1224                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1225                                                         pa_water1.x1(), pa_water1.y1()),
1226                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1227                                                         pa_water1.x1(), pa_water1.y0()),
1228                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1229                                                         pa_water1.x0(), pa_water1.y0()),
1230                                 };
1231                                 
1232                                 /*
1233                                         If our topside is water, set upper border of face
1234                                         at upper border of node
1235                                 */
1236                                 if(top_is_water)
1237                                 {
1238                                         vertices[2].Pos.Y = 0.5*BS;
1239                                         vertices[3].Pos.Y = 0.5*BS;
1240                                 }
1241                                 /*
1242                                         Otherwise upper position of face is corner levels
1243                                 */
1244                                 else
1245                                 {
1246                                         vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
1247                                         vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
1248                                 }
1249                                 
1250                                 /*
1251                                         If neighbor is water, lower border of face is corner
1252                                         water levels
1253                                 */
1254                                 if(neighbor_is_water)
1255                                 {
1256                                         vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
1257                                         vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
1258                                 }
1259                                 /*
1260                                         If neighbor is not water, lower border of face is
1261                                         lower border of node
1262                                 */
1263                                 else
1264                                 {
1265                                         vertices[0].Pos.Y = -0.5*BS;
1266                                         vertices[1].Pos.Y = -0.5*BS;
1267                                 }
1268                                 
1269                                 for(s32 j=0; j<4; j++)
1270                                 {
1271                                         if(dir == v3s16(0,0,1))
1272                                                 vertices[j].Pos.rotateXZBy(0);
1273                                         if(dir == v3s16(0,0,-1))
1274                                                 vertices[j].Pos.rotateXZBy(180);
1275                                         if(dir == v3s16(-1,0,0))
1276                                                 vertices[j].Pos.rotateXZBy(90);
1277                                         if(dir == v3s16(1,0,-0))
1278                                                 vertices[j].Pos.rotateXZBy(-90);
1279
1280                                         vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
1281                                 }
1282
1283                                 u16 indices[] = {0,1,2,2,3,0};
1284                                 // Add to mesh collector
1285                                 collector.append(material_water1, vertices, 4, indices, 6);
1286                         }
1287                         
1288                         /*
1289                                 Generate top side, if appropriate
1290                         */
1291                         
1292                         if(top_is_water == false)
1293                         {
1294                                 video::S3DVertex vertices[4] =
1295                                 {
1296                                         /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1297                                         video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1298                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1299                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1300                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1301                                                         pa_water1.x0(), pa_water1.y1()),
1302                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1303                                                         pa_water1.x1(), pa_water1.y1()),
1304                                         video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1305                                                         pa_water1.x1(), pa_water1.y0()),
1306                                         video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1307                                                         pa_water1.x0(), pa_water1.y0()),
1308                                 };
1309                                 
1310                                 // This fixes a strange bug
1311                                 s32 corner_resolve[4] = {3,2,1,0};
1312
1313                                 for(s32 i=0; i<4; i++)
1314                                 {
1315                                         //vertices[i].Pos.Y += water_level;
1316                                         //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
1317                                         s32 j = corner_resolve[i];
1318                                         vertices[i].Pos.Y += corner_levels[j];
1319                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1320                                 }
1321
1322                                 u16 indices[] = {0,1,2,2,3,0};
1323                                 // Add to mesh collector
1324                                 collector.append(material_water1, vertices, 4, indices, 6);
1325                         }
1326                 }
1327                 /*
1328                         Add water sources to mesh if using new style
1329                 */
1330                 else if(n.d == CONTENT_WATERSOURCE && new_style_water)
1331                 {
1332                         //bool top_is_water = false;
1333                         bool top_is_air = false;
1334                         MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
1335                         /*if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1336                                 top_is_water = true;*/
1337                         if(n.d == CONTENT_AIR)
1338                                 top_is_air = true;
1339                         
1340                         /*if(top_is_water == true)
1341                                 continue;*/
1342                         if(top_is_air == false)
1343                                 continue;
1344
1345                         u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1346                         video::SColor c(WATER_ALPHA,l,l,l);
1347                         
1348                         video::S3DVertex vertices[4] =
1349                         {
1350                                 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
1351                                 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
1352                                 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
1353                                 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
1354                                 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
1355                                                 pa_water1.x0(), pa_water1.y1()),
1356                                 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
1357                                                 pa_water1.x1(), pa_water1.y1()),
1358                                 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
1359                                                 pa_water1.x1(), pa_water1.y0()),
1360                                 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
1361                                                 pa_water1.x0(), pa_water1.y0()),
1362                         };
1363
1364                         for(s32 i=0; i<4; i++)
1365                         {
1366                                 vertices[i].Pos.Y += (-0.5+node_water_level)*BS;
1367                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1368                         }
1369
1370                         u16 indices[] = {0,1,2,2,3,0};
1371                         // Add to mesh collector
1372                         collector.append(material_water1, vertices, 4, indices, 6);
1373                 }
1374                 /*
1375                         Add leaves if using new style
1376                 */
1377                 else if(n.d == CONTENT_LEAVES && new_style_leaves)
1378                 {
1379                         /*u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));*/
1380                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1381                         video::SColor c(255,l,l,l);
1382
1383                         for(u32 j=0; j<6; j++)
1384                         {
1385                                 video::S3DVertex vertices[4] =
1386                                 {
1387                                         /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
1388                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
1389                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
1390                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
1391                                         video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
1392                                                 pa_leaves1.x0(), pa_leaves1.y1()),
1393                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
1394                                                 pa_leaves1.x1(), pa_leaves1.y1()),
1395                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
1396                                                 pa_leaves1.x1(), pa_leaves1.y0()),
1397                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
1398                                                 pa_leaves1.x0(), pa_leaves1.y0()),
1399                                 };
1400
1401                                 if(j == 0)
1402                                 {
1403                                         for(u16 i=0; i<4; i++)
1404                                                 vertices[i].Pos.rotateXZBy(0);
1405                                 }
1406                                 else if(j == 1)
1407                                 {
1408                                         for(u16 i=0; i<4; i++)
1409                                                 vertices[i].Pos.rotateXZBy(180);
1410                                 }
1411                                 else if(j == 2)
1412                                 {
1413                                         for(u16 i=0; i<4; i++)
1414                                                 vertices[i].Pos.rotateXZBy(-90);
1415                                 }
1416                                 else if(j == 3)
1417                                 {
1418                                         for(u16 i=0; i<4; i++)
1419                                                 vertices[i].Pos.rotateXZBy(90);
1420                                 }
1421                                 else if(j == 4)
1422                                 {
1423                                         for(u16 i=0; i<4; i++)
1424                                                 vertices[i].Pos.rotateYZBy(-90);
1425                                 }
1426                                 else if(j == 5)
1427                                 {
1428                                         for(u16 i=0; i<4; i++)
1429                                                 vertices[i].Pos.rotateYZBy(90);
1430                                 }
1431
1432                                 for(u16 i=0; i<4; i++)
1433                                 {
1434                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1435                                 }
1436
1437                                 u16 indices[] = {0,1,2,2,3,0};
1438                                 // Add to mesh collector
1439                                 collector.append(material_leaves1, vertices, 4, indices, 6);
1440                         }
1441                 }
1442         }
1443
1444         /*
1445                 Add stuff from collector to mesh
1446         */
1447         
1448         scene::SMesh *mesh_new = NULL;
1449         mesh_new = new scene::SMesh();
1450         
1451         collector.fillMesh(mesh_new);
1452
1453         /*
1454                 Do some stuff to the mesh
1455         */
1456
1457         mesh_new->recalculateBoundingBox();
1458
1459         /*
1460                 Delete new mesh if it is empty
1461         */
1462
1463         if(mesh_new->getMeshBufferCount() == 0)
1464         {
1465                 mesh_new->drop();
1466                 mesh_new = NULL;
1467         }
1468
1469         if(mesh_new)
1470         {
1471 #if 0
1472                 // Usually 1-700 faces and 1-7 materials
1473                 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1474                                 <<"and uses "<<mesh_new->getMeshBufferCount()
1475                                 <<" materials (meshbuffers)"<<std::endl;
1476 #endif
1477
1478                 // Use VBO for mesh (this just would set this for ever buffer)
1479                 // This will lead to infinite memory usage because or irrlicht.
1480                 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1481
1482                 /*
1483                         NOTE: If that is enabled, some kind of a queue to the main
1484                         thread should be made which would call irrlicht to delete
1485                         the hardware buffer and then delete the mesh
1486                 */
1487         }
1488
1489         return mesh_new;
1490         
1491         //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1492 }
1493
1494 #endif // !SERVER
1495
1496 /*
1497         MapBlock
1498 */
1499
1500 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
1501                 m_parent(parent),
1502                 m_pos(pos),
1503                 changed(true),
1504                 is_underground(false),
1505                 m_lighting_expired(true),
1506                 m_day_night_differs(false),
1507                 //m_not_fully_generated(false),
1508                 m_objects(this)
1509 {
1510         data = NULL;
1511         if(dummy == false)
1512                 reallocate();
1513         
1514         //m_spawn_timer = -10000;
1515
1516 #ifndef SERVER
1517         m_mesh_expired = false;
1518         mesh_mutex.Init();
1519         mesh = NULL;
1520         m_temp_mods_mutex.Init();
1521 #endif
1522 }
1523
1524 MapBlock::~MapBlock()
1525 {
1526 #ifndef SERVER
1527         {
1528                 JMutexAutoLock lock(mesh_mutex);
1529                 
1530                 if(mesh)
1531                 {
1532                         mesh->drop();
1533                         mesh = NULL;
1534                 }
1535         }
1536 #endif
1537
1538         if(data)
1539                 delete[] data;
1540 }
1541
1542 bool MapBlock::isValidPositionParent(v3s16 p)
1543 {
1544         if(isValidPosition(p))
1545         {
1546                 return true;
1547         }
1548         else{
1549                 return m_parent->isValidPosition(getPosRelative() + p);
1550         }
1551 }
1552
1553 MapNode MapBlock::getNodeParent(v3s16 p)
1554 {
1555         if(isValidPosition(p) == false)
1556         {
1557                 return m_parent->getNode(getPosRelative() + p);
1558         }
1559         else
1560         {
1561                 if(data == NULL)
1562                         throw InvalidPositionException();
1563                 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1564         }
1565 }
1566
1567 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
1568 {
1569         if(isValidPosition(p) == false)
1570         {
1571                 m_parent->setNode(getPosRelative() + p, n);
1572         }
1573         else
1574         {
1575                 if(data == NULL)
1576                         throw InvalidPositionException();
1577                 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
1578         }
1579 }
1580
1581 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
1582 {
1583         if(isValidPosition(p) == false)
1584         {
1585                 try{
1586                         return m_parent->getNode(getPosRelative() + p);
1587                 }
1588                 catch(InvalidPositionException &e)
1589                 {
1590                         return MapNode(CONTENT_IGNORE);
1591                 }
1592         }
1593         else
1594         {
1595                 if(data == NULL)
1596                 {
1597                         return MapNode(CONTENT_IGNORE);
1598                 }
1599                 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
1600         }
1601 }
1602
1603 #ifndef SERVER
1604
1605 #if 1
1606 void MapBlock::updateMesh(u32 daynight_ratio)
1607 {
1608 #if 0
1609         /*
1610                 DEBUG: If mesh has been generated, don't generate it again
1611         */
1612         {
1613                 JMutexAutoLock meshlock(mesh_mutex);
1614                 if(mesh != NULL)
1615                         return;
1616         }
1617 #endif
1618
1619         MeshMakeData data;
1620         data.fill(daynight_ratio, this);
1621         
1622         scene::SMesh *mesh_new = makeMapBlockMesh(&data);
1623         
1624         /*
1625                 Replace the mesh
1626         */
1627
1628         replaceMesh(mesh_new);
1629
1630 }
1631 #endif
1632
1633 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
1634 {
1635         mesh_mutex.Lock();
1636
1637         //scene::SMesh *mesh_old = mesh[daynight_i];
1638         //mesh[daynight_i] = mesh_new;
1639
1640         scene::SMesh *mesh_old = mesh;
1641         mesh = mesh_new;
1642         setMeshExpired(false);
1643         
1644         if(mesh_old != NULL)
1645         {
1646                 // Remove hardware buffers of meshbuffers of mesh
1647                 // NOTE: No way, this runs in a different thread and everything
1648                 /*u32 c = mesh_old->getMeshBufferCount();
1649                 for(u32 i=0; i<c; i++)
1650                 {
1651                         IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1652                 }*/
1653                 
1654                 /*dstream<<"mesh_old->getReferenceCount()="
1655                                 <<mesh_old->getReferenceCount()<<std::endl;
1656                 u32 c = mesh_old->getMeshBufferCount();
1657                 for(u32 i=0; i<c; i++)
1658                 {
1659                         scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1660                         dstream<<"buf->getReferenceCount()="
1661                                         <<buf->getReferenceCount()<<std::endl;
1662                 }*/
1663
1664                 // Drop the mesh
1665                 mesh_old->drop();
1666
1667                 //delete mesh_old;
1668         }
1669
1670         mesh_mutex.Unlock();
1671 }
1672         
1673 #endif // !SERVER
1674
1675 /*
1676         Propagates sunlight down through the block.
1677         Doesn't modify nodes that are not affected by sunlight.
1678         
1679         Returns false if sunlight at bottom block is invalid
1680         Returns true if bottom block doesn't exist.
1681
1682         If there is a block above, continues from it.
1683         If there is no block above, assumes there is sunlight, unless
1684         is_underground is set or highest node is water.
1685
1686         At the moment, all sunlighted nodes are added to light_sources.
1687         - SUGG: This could be optimized
1688
1689         Turns sunglighted mud into grass.
1690
1691         if remove_light==true, sets non-sunlighted nodes black.
1692
1693         if black_air_left!=NULL, it is set to true if non-sunlighted
1694         air is left in block.
1695 */
1696 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1697                 bool remove_light, bool *black_air_left,
1698                 bool grow_grass)
1699 {
1700         // Whether the sunlight at the top of the bottom block is valid
1701         bool block_below_is_valid = true;
1702         
1703         v3s16 pos_relative = getPosRelative();
1704         
1705         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1706         {
1707                 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1708                 {
1709 #if 1
1710                         bool no_sunlight = false;
1711                         bool no_top_block = false;
1712                         // Check if node above block has sunlight
1713                         try{
1714                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1715                                 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1716                                 {
1717                                         no_sunlight = true;
1718                                 }
1719                         }
1720                         catch(InvalidPositionException &e)
1721                         {
1722                                 no_top_block = true;
1723                                 
1724                                 // NOTE: This makes over-ground roofed places sunlighted
1725                                 // Assume sunlight, unless is_underground==true
1726                                 if(is_underground)
1727                                 {
1728                                         no_sunlight = true;
1729                                 }
1730                                 else
1731                                 {
1732                                         MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1733                                         if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1734                                         {
1735                                                 no_sunlight = true;
1736                                         }
1737                                 }
1738                                 // NOTE: As of now, this just would make everything dark.
1739                                 // No sunlight here
1740                                 //no_sunlight = true;
1741                         }
1742 #endif
1743 #if 0 // Doesn't work; nothing gets light.
1744                         bool no_sunlight = true;
1745                         bool no_top_block = false;
1746                         // Check if node above block has sunlight
1747                         try{
1748                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1749                                 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1750                                 {
1751                                         no_sunlight = false;
1752                                 }
1753                         }
1754                         catch(InvalidPositionException &e)
1755                         {
1756                                 no_top_block = true;
1757                         }
1758 #endif
1759
1760                         /*std::cout<<"("<<x<<","<<z<<"): "
1761                                         <<"no_top_block="<<no_top_block
1762                                         <<", is_underground="<<is_underground
1763                                         <<", no_sunlight="<<no_sunlight
1764                                         <<std::endl;*/
1765                 
1766                         s16 y = MAP_BLOCKSIZE-1;
1767                         
1768                         // This makes difference to diminishing in water.
1769                         bool stopped_to_solid_object = false;
1770                         
1771                         u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1772
1773                         for(; y >= 0; y--)
1774                         {
1775                                 v3s16 pos(x, y, z);
1776                                 MapNode &n = getNodeRef(pos);
1777                                 
1778                                 if(current_light == 0)
1779                                 {
1780                                         // Do nothing
1781                                 }
1782                                 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1783                                 {
1784                                         // Do nothing: Sunlight is continued
1785                                 }
1786                                 else if(n.light_propagates() == false)
1787                                 {
1788                                         if(grow_grass)
1789                                         {
1790                                                 bool upper_is_air = false;
1791                                                 try
1792                                                 {
1793                                                         if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1794                                                                 upper_is_air = true;
1795                                                 }
1796                                                 catch(InvalidPositionException &e)
1797                                                 {
1798                                                 }
1799                                                 // Turn mud into grass
1800                                                 if(upper_is_air && n.d == CONTENT_MUD
1801                                                                 && current_light == LIGHT_SUN)
1802                                                 {
1803                                                         n.d = CONTENT_GRASS;
1804                                                 }
1805                                         }
1806
1807                                         // A solid object is on the way.
1808                                         stopped_to_solid_object = true;
1809                                         
1810                                         // Light stops.
1811                                         current_light = 0;
1812                                 }
1813                                 else
1814                                 {
1815                                         // Diminish light
1816                                         current_light = diminish_light(current_light);
1817                                 }
1818
1819                                 u8 old_light = n.getLight(LIGHTBANK_DAY);
1820
1821                                 if(current_light > old_light || remove_light)
1822                                 {
1823                                         n.setLight(LIGHTBANK_DAY, current_light);
1824                                 }
1825                                 
1826                                 if(diminish_light(current_light) != 0)
1827                                 {
1828                                         light_sources.insert(pos_relative + pos, true);
1829                                 }
1830
1831                                 if(current_light == 0 && stopped_to_solid_object)
1832                                 {
1833                                         if(black_air_left)
1834                                         {
1835                                                 *black_air_left = true;
1836                                         }
1837                                 }
1838                         }
1839
1840                         // Whether or not the block below should see LIGHT_SUN
1841                         bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1842
1843                         /*
1844                                 If the block below hasn't already been marked invalid:
1845
1846                                 Check if the node below the block has proper sunlight at top.
1847                                 If not, the block below is invalid.
1848                                 
1849                                 Ignore non-transparent nodes as they always have no light
1850                         */
1851                         try
1852                         {
1853                         if(block_below_is_valid)
1854                         {
1855                                 MapNode n = getNodeParent(v3s16(x, -1, z));
1856                                 if(n.light_propagates())
1857                                 {
1858                                         if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1859                                                         && sunlight_should_go_down == false)
1860                                                 block_below_is_valid = false;
1861                                         else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1862                                                         && sunlight_should_go_down == true)
1863                                                 block_below_is_valid = false;
1864                                 }
1865                         }//if
1866                         }//try
1867                         catch(InvalidPositionException &e)
1868                         {
1869                                 /*std::cout<<"InvalidBlockException for bottom block node"
1870                                                 <<std::endl;*/
1871                                 // Just no block below, no need to panic.
1872                         }
1873                 }
1874         }
1875
1876         return block_below_is_valid;
1877 }
1878
1879
1880 void MapBlock::copyTo(VoxelManipulator &dst)
1881 {
1882         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1883         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1884         
1885         // Copy from data to VoxelManipulator
1886         dst.copyFrom(data, data_area, v3s16(0,0,0),
1887                         getPosRelative(), data_size);
1888 }
1889
1890 void MapBlock::copyFrom(VoxelManipulator &dst)
1891 {
1892         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1893         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1894         
1895         // Copy from VoxelManipulator to data
1896         dst.copyTo(data, data_area, v3s16(0,0,0),
1897                         getPosRelative(), data_size);
1898 }
1899
1900 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1901 {
1902         /*
1903                 Step objects
1904         */
1905         m_objects.step(dtime, server, daynight_ratio);
1906
1907 #if 0
1908         /*
1909                 Spawn some objects at random.
1910
1911                 Use dayNightDiffed() to approximate being near ground level
1912         */
1913         if(m_spawn_timer < -999)
1914         {
1915                 m_spawn_timer = 60;
1916         }
1917         if(dayNightDiffed() == true && getObjectCount() == 0)
1918         {
1919                 m_spawn_timer -= dtime;
1920                 if(m_spawn_timer <= 0.0)
1921                 {
1922                         m_spawn_timer += myrand() % 300;
1923                         
1924                         v2s16 p2d(
1925                                 (myrand()%(MAP_BLOCKSIZE-1))+0,
1926                                 (myrand()%(MAP_BLOCKSIZE-1))+0
1927                         );
1928
1929                         s16 y = getGroundLevel(p2d);
1930                         
1931                         if(y >= 0)
1932                         {
1933                                 v3s16 p(p2d.X, y+1, p2d.Y);
1934
1935                                 if(getNode(p).d == CONTENT_AIR
1936                                                 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1937                                 {
1938                                         RatObject *obj = new RatObject(NULL, -1, intToFloat(p, BS));
1939                                         addObject(obj);
1940                                 }
1941                         }
1942                 }
1943         }
1944 #endif
1945
1946         setChangedFlag();
1947 }
1948
1949
1950 void MapBlock::updateDayNightDiff()
1951 {
1952         if(data == NULL)
1953         {
1954                 m_day_night_differs = false;
1955                 return;
1956         }
1957
1958         bool differs = false;
1959
1960         /*
1961                 Check if any lighting value differs
1962         */
1963         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1964         {
1965                 MapNode &n = data[i];
1966                 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1967                 {
1968                         differs = true;
1969                         break;
1970                 }
1971         }
1972
1973         /*
1974                 If some lighting values differ, check if the whole thing is
1975                 just air. If it is, differ = false
1976         */
1977         if(differs)
1978         {
1979                 bool only_air = true;
1980                 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1981                 {
1982                         MapNode &n = data[i];
1983                         if(n.d != CONTENT_AIR)
1984                         {
1985                                 only_air = false;
1986                                 break;
1987                         }
1988                 }
1989                 if(only_air)
1990                         differs = false;
1991         }
1992
1993         // Set member variable
1994         m_day_night_differs = differs;
1995 }
1996
1997 s16 MapBlock::getGroundLevel(v2s16 p2d)
1998 {
1999         if(isDummy())
2000                 return -3;
2001         try
2002         {
2003                 s16 y = MAP_BLOCKSIZE-1;
2004                 for(; y>=0; y--)
2005                 {
2006                         //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
2007                         if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
2008                         {
2009                                 if(y == MAP_BLOCKSIZE-1)
2010                                         return -2;
2011                                 else
2012                                         return y;
2013                         }
2014                 }
2015                 return -1;
2016         }
2017         catch(InvalidPositionException &e)
2018         {
2019                 return -3;
2020         }
2021 }
2022
2023 /*
2024         Serialization
2025 */
2026
2027 void MapBlock::serialize(std::ostream &os, u8 version)
2028 {
2029         if(!ser_ver_supported(version))
2030                 throw VersionMismatchException("ERROR: MapBlock format not supported");
2031         
2032         if(data == NULL)
2033         {
2034                 throw SerializationError("ERROR: Not writing dummy block.");
2035         }
2036         
2037         // These have no compression
2038         if(version <= 3 || version == 5 || version == 6)
2039         {
2040                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2041                 
2042                 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
2043                 SharedBuffer<u8> dest(buflen);
2044
2045                 dest[0] = is_underground;
2046                 for(u32 i=0; i<nodecount; i++)
2047                 {
2048                         u32 s = 1 + i * MapNode::serializedLength(version);
2049                         data[i].serialize(&dest[s], version);
2050                 }
2051                 
2052                 os.write((char*)*dest, dest.getSize());
2053         }
2054         else if(version <= 10)
2055         {
2056                 /*
2057                         With compression.
2058                         Compress the materials and the params separately.
2059                 */
2060                 
2061                 // First byte
2062                 os.write((char*)&is_underground, 1);
2063
2064                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2065
2066                 // Get and compress materials
2067                 SharedBuffer<u8> materialdata(nodecount);
2068                 for(u32 i=0; i<nodecount; i++)
2069                 {
2070                         materialdata[i] = data[i].d;
2071                 }
2072                 compress(materialdata, os, version);
2073
2074                 // Get and compress lights
2075                 SharedBuffer<u8> lightdata(nodecount);
2076                 for(u32 i=0; i<nodecount; i++)
2077                 {
2078                         lightdata[i] = data[i].param;
2079                 }
2080                 compress(lightdata, os, version);
2081                 
2082                 if(version >= 10)
2083                 {
2084                         // Get and compress param2
2085                         SharedBuffer<u8> param2data(nodecount);
2086                         for(u32 i=0; i<nodecount; i++)
2087                         {
2088                                 param2data[i] = data[i].param2;
2089                         }
2090                         compress(param2data, os, version);
2091                 }
2092         }
2093         // All other versions (newest)
2094         else
2095         {
2096                 // First byte
2097                 u8 flags = 0;
2098                 if(is_underground)
2099                         flags |= 0x01;
2100                 if(m_day_night_differs)
2101                         flags |= 0x02;
2102                 if(m_lighting_expired)
2103                         flags |= 0x04;
2104                 os.write((char*)&flags, 1);
2105
2106                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2107
2108                 /*
2109                         Get data
2110                 */
2111
2112                 SharedBuffer<u8> databuf(nodecount*3);
2113
2114                 // Get contents
2115                 for(u32 i=0; i<nodecount; i++)
2116                 {
2117                         databuf[i] = data[i].d;
2118                 }
2119
2120                 // Get params
2121                 for(u32 i=0; i<nodecount; i++)
2122                 {
2123                         databuf[i+nodecount] = data[i].param;
2124                 }
2125
2126                 // Get param2
2127                 for(u32 i=0; i<nodecount; i++)
2128                 {
2129                         databuf[i+nodecount*2] = data[i].param2;
2130                 }
2131
2132                 /*
2133                         Compress data to output stream
2134                 */
2135
2136                 compress(databuf, os, version);
2137                 
2138                 /*
2139                         NodeMetadata
2140                 */
2141                 if(version >= 14)
2142                 {
2143                         if(version <= 15)
2144                         {
2145                                 std::ostringstream oss(std::ios_base::binary);
2146                                 m_node_metadata.serialize(oss);
2147                                 os<<serializeString(oss.str());
2148                         }
2149                         else
2150                         {
2151                                 std::ostringstream oss(std::ios_base::binary);
2152                                 m_node_metadata.serialize(oss);
2153                                 compressZlib(oss.str(), os);
2154                                 //os<<serializeLongString(oss.str());
2155                         }
2156                 }
2157         }
2158 }
2159
2160 void MapBlock::deSerialize(std::istream &is, u8 version)
2161 {
2162         if(!ser_ver_supported(version))
2163                 throw VersionMismatchException("ERROR: MapBlock format not supported");
2164
2165         // These have no lighting info
2166         if(version <= 1)
2167         {
2168                 setLightingExpired(true);
2169         }
2170
2171         // These have no compression
2172         if(version <= 3 || version == 5 || version == 6)
2173         {
2174                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2175                 char tmp;
2176                 is.read(&tmp, 1);
2177                 if(is.gcount() != 1)
2178                         throw SerializationError
2179                                         ("MapBlock::deSerialize: no enough input data");
2180                 is_underground = tmp;
2181                 for(u32 i=0; i<nodecount; i++)
2182                 {
2183                         s32 len = MapNode::serializedLength(version);
2184                         SharedBuffer<u8> d(len);
2185                         is.read((char*)*d, len);
2186                         if(is.gcount() != len)
2187                                 throw SerializationError
2188                                                 ("MapBlock::deSerialize: no enough input data");
2189                         data[i].deSerialize(*d, version);
2190                 }
2191         }
2192         else if(version <= 10)
2193         {
2194                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2195
2196                 u8 t8;
2197                 is.read((char*)&t8, 1);
2198                 is_underground = t8;
2199
2200                 {
2201                         // Uncompress and set material data
2202                         std::ostringstream os(std::ios_base::binary);
2203                         decompress(is, os, version);
2204                         std::string s = os.str();
2205                         if(s.size() != nodecount)
2206                                 throw SerializationError
2207                                                 ("MapBlock::deSerialize: invalid format");
2208                         for(u32 i=0; i<s.size(); i++)
2209                         {
2210                                 data[i].d = s[i];
2211                         }
2212                 }
2213                 {
2214                         // Uncompress and set param data
2215                         std::ostringstream os(std::ios_base::binary);
2216                         decompress(is, os, version);
2217                         std::string s = os.str();
2218                         if(s.size() != nodecount)
2219                                 throw SerializationError
2220                                                 ("MapBlock::deSerialize: invalid format");
2221                         for(u32 i=0; i<s.size(); i++)
2222                         {
2223                                 data[i].param = s[i];
2224                         }
2225                 }
2226         
2227                 if(version >= 10)
2228                 {
2229                         // Uncompress and set param2 data
2230                         std::ostringstream os(std::ios_base::binary);
2231                         decompress(is, os, version);
2232                         std::string s = os.str();
2233                         if(s.size() != nodecount)
2234                                 throw SerializationError
2235                                                 ("MapBlock::deSerialize: invalid format");
2236                         for(u32 i=0; i<s.size(); i++)
2237                         {
2238                                 data[i].param2 = s[i];
2239                         }
2240                 }
2241         }
2242         // All other versions (newest)
2243         else
2244         {
2245                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
2246
2247                 u8 flags;
2248                 is.read((char*)&flags, 1);
2249                 is_underground = (flags & 0x01) ? true : false;
2250                 m_day_night_differs = (flags & 0x02) ? true : false;
2251                 m_lighting_expired = (flags & 0x04) ? true : false;
2252
2253                 // Uncompress data
2254                 std::ostringstream os(std::ios_base::binary);
2255                 decompress(is, os, version);
2256                 std::string s = os.str();
2257                 if(s.size() != nodecount*3)
2258                         throw SerializationError
2259                                         ("MapBlock::deSerialize: invalid format");
2260
2261                 // Set contents
2262                 for(u32 i=0; i<nodecount; i++)
2263                 {
2264                         data[i].d = s[i];
2265                 }
2266                 // Set params
2267                 for(u32 i=0; i<nodecount; i++)
2268                 {
2269                         data[i].param = s[i+nodecount];
2270                 }
2271                 // Set param2
2272                 for(u32 i=0; i<nodecount; i++)
2273                 {
2274                         data[i].param2 = s[i+nodecount*2];
2275                 }
2276                 
2277                 /*
2278                         NodeMetadata
2279                 */
2280                 if(version >= 14)
2281                 {
2282                         // Ignore errors
2283                         try{
2284                                 if(version <= 15)
2285                                 {
2286                                         std::string data = deSerializeString(is);
2287                                         std::istringstream iss(data, std::ios_base::binary);
2288                                         m_node_metadata.deSerialize(iss);
2289                                 }
2290                                 else
2291                                 {
2292                                         //std::string data = deSerializeLongString(is);
2293                                         std::ostringstream oss(std::ios_base::binary);
2294                                         decompressZlib(is, oss);
2295                                         std::istringstream iss(oss.str(), std::ios_base::binary);
2296                                         m_node_metadata.deSerialize(iss);
2297                                 }
2298                         }
2299                         catch(SerializationError &e)
2300                         {
2301                                 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
2302                                                 <<" while deserializing node metadata"<<std::endl;
2303                         }
2304                 }
2305         }
2306         
2307         /*
2308                 Translate nodes as specified in the translate_to fields of
2309                 node features
2310         */
2311         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
2312         {
2313                 MapNode &n = data[i];
2314
2315                 MapNode *translate_to = content_features(n.d).translate_to;
2316                 if(translate_to)
2317                 {
2318                         dstream<<"MapBlock: WARNING: Translating node "<<n.d<<" to "
2319                                         <<translate_to->d<<std::endl;
2320                         n = *translate_to;
2321                 }
2322         }
2323 }
2324
2325
2326 //END