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