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