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