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