Improve rendering and fix tiling in mesh generation
[oweals/minetest.git] / src / mapblock_mesh.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 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_mesh.h"
21 #include "light.h"
22 #include "mapblock.h"
23 #include "map.h"
24 #include "main.h" // For g_settings and g_texturesource
25 #include "content_mapblock.h"
26 #include "settings.h"
27 #include "profiler.h"
28
29 void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block)
30 {
31         m_daynight_ratio = daynight_ratio;
32         m_blockpos = block->getPos();
33
34         v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
35         
36         /*
37                 There is no harm not copying the TempMods of the neighbors
38                 because they are already copied to this block
39         */
40         m_temp_mods.clear();
41         block->copyTempMods(m_temp_mods);
42         
43         /*
44                 Copy data
45         */
46
47         // Allocate this block + neighbors
48         m_vmanip.clear();
49         m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
50                         blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
51
52         {
53                 //TimeTaker timer("copy central block data");
54                 // 0ms
55
56                 // Copy our data
57                 block->copyTo(m_vmanip);
58         }
59         {
60                 //TimeTaker timer("copy neighbor block data");
61                 // 0ms
62
63                 /*
64                         Copy neighbors. This is lightning fast.
65                         Copying only the borders would be *very* slow.
66                 */
67                 
68                 // Get map
69                 Map *map = block->getParent();
70
71                 for(u16 i=0; i<6; i++)
72                 {
73                         const v3s16 &dir = g_6dirs[i];
74                         v3s16 bp = m_blockpos + dir;
75                         MapBlock *b = map->getBlockNoCreateNoEx(bp);
76                         if(b)
77                                 b->copyTo(m_vmanip);
78                 }
79         }
80 }
81
82 /*
83         vertex_dirs: v3s16[4]
84 */
85 void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
86 {
87         /*
88                 If looked from outside the node towards the face, the corners are:
89                 0: bottom-right
90                 1: bottom-left
91                 2: top-left
92                 3: top-right
93         */
94         if(dir == v3s16(0,0,1))
95         {
96                 // If looking towards z+, this is the face that is behind
97                 // the center point, facing towards z+.
98                 vertex_dirs[0] = v3s16(-1,-1, 1);
99                 vertex_dirs[1] = v3s16( 1,-1, 1);
100                 vertex_dirs[2] = v3s16( 1, 1, 1);
101                 vertex_dirs[3] = v3s16(-1, 1, 1);
102         }
103         else if(dir == v3s16(0,0,-1))
104         {
105                 // faces towards Z-
106                 vertex_dirs[0] = v3s16( 1,-1,-1);
107                 vertex_dirs[1] = v3s16(-1,-1,-1);
108                 vertex_dirs[2] = v3s16(-1, 1,-1);
109                 vertex_dirs[3] = v3s16( 1, 1,-1);
110         }
111         else if(dir == v3s16(1,0,0))
112         {
113                 // faces towards X+
114                 vertex_dirs[0] = v3s16( 1,-1, 1);
115                 vertex_dirs[1] = v3s16( 1,-1,-1);
116                 vertex_dirs[2] = v3s16( 1, 1,-1);
117                 vertex_dirs[3] = v3s16( 1, 1, 1);
118         }
119         else if(dir == v3s16(-1,0,0))
120         {
121                 // faces towards X-
122                 vertex_dirs[0] = v3s16(-1,-1,-1);
123                 vertex_dirs[1] = v3s16(-1,-1, 1);
124                 vertex_dirs[2] = v3s16(-1, 1, 1);
125                 vertex_dirs[3] = v3s16(-1, 1,-1);
126         }
127         else if(dir == v3s16(0,1,0))
128         {
129                 // faces towards Y+ (assume Z- as "down" in texture)
130                 vertex_dirs[0] = v3s16( 1, 1,-1);
131                 vertex_dirs[1] = v3s16(-1, 1,-1);
132                 vertex_dirs[2] = v3s16(-1, 1, 1);
133                 vertex_dirs[3] = v3s16( 1, 1, 1);
134         }
135         else if(dir == v3s16(0,-1,0))
136         {
137                 // faces towards Y- (assume Z+ as "down" in texture)
138                 vertex_dirs[0] = v3s16( 1,-1, 1);
139                 vertex_dirs[1] = v3s16(-1,-1, 1);
140                 vertex_dirs[2] = v3s16(-1,-1,-1);
141                 vertex_dirs[3] = v3s16( 1,-1,-1);
142         }
143 }
144
145 video::SColor MapBlock_LightColor(u8 alpha, u8 light)
146 {
147 #if 0
148         return video::SColor(alpha,light,light,light);
149 #endif
150         //return video::SColor(alpha,light,light,MYMAX(0, (s16)light-25)+25);
151         /*return video::SColor(alpha,light,light,MYMAX(0,
152                         pow((float)light/255.0, 0.8)*255.0));*/
153 #if 1
154         // Emphase blue a bit in darker places
155         float lim = 80;
156         float power = 0.8;
157         if(light > lim)
158                 return video::SColor(alpha,light,light,light);
159         else
160                 return video::SColor(alpha,light,light,MYMAX(0,
161                                 pow((float)light/lim, power)*lim));
162 #endif
163 }
164
165 struct FastFace
166 {
167         TileSpec tile;
168         video::S3DVertex vertices[4]; // Precalculated vertices
169 };
170
171 void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p,
172                 v3s16 dir, v3f scale, v3f posRelative_f,
173                 core::array<FastFace> &dest)
174 {
175         FastFace face;
176         
177         // Position is at the center of the cube.
178         v3f pos = p * BS;
179         posRelative_f *= BS;
180
181         v3f vertex_pos[4];
182         v3s16 vertex_dirs[4];
183         getNodeVertexDirs(dir, vertex_dirs);
184         for(u16 i=0; i<4; i++)
185         {
186                 vertex_pos[i] = v3f(
187                                 BS/2*vertex_dirs[i].X,
188                                 BS/2*vertex_dirs[i].Y,
189                                 BS/2*vertex_dirs[i].Z
190                 );
191         }
192
193         for(u16 i=0; i<4; i++)
194         {
195                 vertex_pos[i].X *= scale.X;
196                 vertex_pos[i].Y *= scale.Y;
197                 vertex_pos[i].Z *= scale.Z;
198                 vertex_pos[i] += pos + posRelative_f;
199         }
200
201         f32 abs_scale = 1.;
202         if     (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
203         else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
204         else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
205
206         v3f zerovector = v3f(0,0,0);
207         
208         u8 alpha = tile.alpha;
209         /*u8 alpha = 255;
210         if(tile.id == TILE_WATER)
211                 alpha = WATER_ALPHA;*/
212
213         float x0 = tile.texture.pos.X;
214         float y0 = tile.texture.pos.Y;
215         float w = tile.texture.size.X;
216         float h = tile.texture.size.Y;
217
218         /*video::SColor c = MapBlock_LightColor(alpha, li);
219
220         face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), c,
221                         core::vector2d<f32>(x0+w*abs_scale, y0+h));
222         face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), c,
223                         core::vector2d<f32>(x0, y0+h));
224         face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), c,
225                         core::vector2d<f32>(x0, y0));
226         face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c,
227                         core::vector2d<f32>(x0+w*abs_scale, y0));*/
228
229         face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0),
230                         MapBlock_LightColor(alpha, li0),
231                         core::vector2d<f32>(x0+w*abs_scale, y0+h));
232         face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0),
233                         MapBlock_LightColor(alpha, li1),
234                         core::vector2d<f32>(x0, y0+h));
235         face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0),
236                         MapBlock_LightColor(alpha, li2),
237                         core::vector2d<f32>(x0, y0));
238         face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0),
239                         MapBlock_LightColor(alpha, li3),
240                         core::vector2d<f32>(x0+w*abs_scale, y0));
241
242         face.tile = tile;
243         //DEBUG
244         //f->tile = TILE_STONE;
245         
246         dest.push_back(face);
247 }
248         
249 /*
250         Gets node tile from any place relative to block.
251         Returns TILE_NODE if doesn't exist or should not be drawn.
252 */
253 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
254                 NodeModMap &temp_mods)
255 {
256         TileSpec spec;
257         spec = mn.getTile(face_dir);
258         
259         /*
260                 Check temporary modifications on this node
261         */
262         /*core::map<v3s16, NodeMod>::Node *n;
263         n = m_temp_mods.find(p);
264         // If modified
265         if(n != NULL)
266         {
267                 struct NodeMod mod = n->getValue();*/
268         NodeMod mod;
269         if(temp_mods.get(p, &mod))
270         {
271                 if(mod.type == NODEMOD_CHANGECONTENT)
272                 {
273                         MapNode mn2(mod.param);
274                         spec = mn2.getTile(face_dir);
275                 }
276                 if(mod.type == NODEMOD_CRACK)
277                 {
278                         /*
279                                 Get texture id, translate it to name, append stuff to
280                                 name, get texture id
281                         */
282
283                         // Get original texture name
284                         u32 orig_id = spec.texture.id;
285                         std::string orig_name = g_texturesource->getTextureName(orig_id);
286
287                         // Create new texture name
288                         std::ostringstream os;
289                         os<<orig_name<<"^[crack"<<mod.param;
290
291                         // Get new texture
292                         u32 new_id = g_texturesource->getTextureId(os.str());
293                         
294                         /*dstream<<"MapBlock::getNodeTile(): Switching from "
295                                         <<orig_name<<" to "<<os.str()<<" ("
296                                         <<orig_id<<" to "<<new_id<<")"<<std::endl;*/
297                         
298                         spec.texture = g_texturesource->getTexture(new_id);
299                 }
300         }
301         
302         return spec;
303 }
304
305 content_t getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
306 {
307         /*
308                 Check temporary modifications on this node
309         */
310         /*core::map<v3s16, NodeMod>::Node *n;
311         n = m_temp_mods.find(p);
312         // If modified
313         if(n != NULL)
314         {
315                 struct NodeMod mod = n->getValue();*/
316         NodeMod mod;
317         if(temp_mods.get(p, &mod))
318         {
319                 if(mod.type == NODEMOD_CHANGECONTENT)
320                 {
321                         // Overrides content
322                         return mod.param;
323                 }
324                 if(mod.type == NODEMOD_CRACK)
325                 {
326                         /*
327                                 Content doesn't change.
328                                 
329                                 face_contents works just like it should, because
330                                 there should not be faces between differently cracked
331                                 nodes.
332
333                                 If a semi-transparent node is cracked in front an
334                                 another one, it really doesn't matter whether there
335                                 is a cracked face drawn in between or not.
336                         */
337                 }
338         }
339
340         return mn.getContent();
341 }
342
343 v3s16 dirs8[8] = {
344         v3s16(0,0,0),
345         v3s16(0,0,1),
346         v3s16(0,1,0),
347         v3s16(0,1,1),
348         v3s16(1,0,0),
349         v3s16(1,1,0),
350         v3s16(1,0,1),
351         v3s16(1,1,1),
352 };
353
354 // Calculate lighting at the XYZ- corner of p
355 u8 getSmoothLight(v3s16 p, VoxelManipulator &vmanip, u32 daynight_ratio)
356 {
357         u16 ambient_occlusion = 0;
358         u16 light = 0;
359         u16 light_count = 0;
360         for(u32 i=0; i<8; i++)
361         {
362                 MapNode n = vmanip.getNodeNoEx(p - dirs8[i]);
363                 if(content_features(n).param_type == CPT_LIGHT
364                                 // Fast-style leaves look better this way
365                                 && content_features(n).solidness != 2)
366                 {
367                         light += decode_light(n.getLightBlend(daynight_ratio));
368                         light_count++;
369                 }
370                 else
371                 {
372                         if(n.getContent() != CONTENT_IGNORE)
373                                 ambient_occlusion++;
374                 }
375         }
376
377         if(light_count == 0)
378                 return 255;
379         
380         light /= light_count;
381
382         if(ambient_occlusion > 4)
383         {
384                 ambient_occlusion -= 4;
385                 light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
386         }
387
388         return light;
389 }
390
391 // Calculate lighting at the given corner of p
392 u8 getSmoothLight(v3s16 p, v3s16 corner,
393                 VoxelManipulator &vmanip, u32 daynight_ratio)
394 {
395         if(corner.X == 1) p.X += 1;
396         else              assert(corner.X == -1);
397         if(corner.Y == 1) p.Y += 1;
398         else              assert(corner.Y == -1);
399         if(corner.Z == 1) p.Z += 1;
400         else              assert(corner.Z == -1);
401         
402         return getSmoothLight(p, vmanip, daynight_ratio);
403 }
404
405 void getTileInfo(
406                 // Input:
407                 v3s16 blockpos_nodes,
408                 v3s16 p,
409                 v3s16 face_dir,
410                 u32 daynight_ratio,
411                 VoxelManipulator &vmanip,
412                 NodeModMap &temp_mods,
413                 bool smooth_lighting,
414                 // Output:
415                 bool &makes_face,
416                 v3s16 &p_corrected,
417                 v3s16 &face_dir_corrected,
418                 u8 *lights,
419                 TileSpec &tile
420         )
421 {
422         MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
423         MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
424         TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
425         TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
426         
427         // This is hackish
428         content_t content0 = getNodeContent(p, n0, temp_mods);
429         content_t content1 = getNodeContent(p + face_dir, n1, temp_mods);
430         u8 mf = face_contents(content0, content1);
431
432         if(mf == 0)
433         {
434                 makes_face = false;
435                 return;
436         }
437
438         makes_face = true;
439         
440         if(mf == 1)
441         {
442                 tile = tile0;
443                 p_corrected = p;
444                 face_dir_corrected = face_dir;
445         }
446         else
447         {
448                 tile = tile1;
449                 p_corrected = p + face_dir;
450                 face_dir_corrected = -face_dir;
451         }
452         
453         if(smooth_lighting == false)
454         {
455                 lights[0] = lights[1] = lights[2] = lights[3] =
456                                 decode_light(getFaceLight(daynight_ratio, n0, n1, face_dir));
457         }
458         else
459         {
460                 v3s16 vertex_dirs[4];
461                 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
462                 for(u16 i=0; i<4; i++)
463                 {
464                         lights[i] = getSmoothLight(blockpos_nodes + p_corrected,
465                                         vertex_dirs[i], vmanip, daynight_ratio);
466                 }
467         }
468         
469         return;
470 }
471
472 /*
473         startpos:
474         translate_dir: unit vector with only one of x, y or z
475         face_dir: unit vector with only one of x, y or z
476 */
477 void updateFastFaceRow(
478                 u32 daynight_ratio,
479                 v3f posRelative_f,
480                 v3s16 startpos,
481                 u16 length,
482                 v3s16 translate_dir,
483                 v3f translate_dir_f,
484                 v3s16 face_dir,
485                 v3f face_dir_f,
486                 core::array<FastFace> &dest,
487                 NodeModMap &temp_mods,
488                 VoxelManipulator &vmanip,
489                 v3s16 blockpos_nodes,
490                 bool smooth_lighting)
491 {
492         v3s16 p = startpos;
493         
494         u16 continuous_tiles_count = 0;
495         
496         bool makes_face = false;
497         v3s16 p_corrected;
498         v3s16 face_dir_corrected;
499         u8 lights[4] = {0,0,0,0};
500         TileSpec tile;
501         getTileInfo(blockpos_nodes, p, face_dir, daynight_ratio,
502                         vmanip, temp_mods, smooth_lighting,
503                         makes_face, p_corrected, face_dir_corrected, lights, tile);
504
505         for(u16 j=0; j<length; j++)
506         {
507                 // If tiling can be done, this is set to false in the next step
508                 bool next_is_different = true;
509                 
510                 v3s16 p_next;
511                 
512                 bool next_makes_face = false;
513                 v3s16 next_p_corrected;
514                 v3s16 next_face_dir_corrected;
515                 u8 next_lights[4] = {0,0,0,0};
516                 TileSpec next_tile;
517                 
518                 // If at last position, there is nothing to compare to and
519                 // the face must be drawn anyway
520                 if(j != length - 1)
521                 {
522                         p_next = p + translate_dir;
523                         
524                         getTileInfo(blockpos_nodes, p_next, face_dir, daynight_ratio,
525                                         vmanip, temp_mods, smooth_lighting,
526                                         next_makes_face, next_p_corrected,
527                                         next_face_dir_corrected, next_lights,
528                                         next_tile);
529                         
530                         if(next_makes_face == makes_face
531                                         && next_p_corrected == p_corrected + translate_dir
532                                         && next_face_dir_corrected == face_dir_corrected
533                                         && next_lights[0] == lights[0]
534                                         && next_lights[1] == lights[1]
535                                         && next_lights[2] == lights[2]
536                                         && next_lights[3] == lights[3]
537                                         && next_tile == tile)
538                         {
539                                 next_is_different = false;
540                         }
541                         else{
542                                 /*if(makes_face){
543                                         g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
544                                                         next_makes_face != makes_face ? 1 : 0);
545                                         g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
546                                                         (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
547                                         g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
548                                                         next_face_dir_corrected != face_dir_corrected ? 1 : 0);
549                                         g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
550                                                         (next_lights[0] != lights[0] ||
551                                                         next_lights[0] != lights[0] ||
552                                                         next_lights[0] != lights[0] ||
553                                                         next_lights[0] != lights[0]) ? 1 : 0);
554                                         g_profiler->add("Meshgen: diff: !(next_tile == tile)",
555                                                         !(next_tile == tile) ? 1 : 0);
556                                 }*/
557                         }
558                         /*g_profiler->add("Meshgen: Total faces checked", 1);
559                         if(makes_face)
560                                 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
561                 } else {
562                         /*if(makes_face)
563                                 g_profiler->add("Meshgen: diff: last position", 1);*/
564                 }
565
566                 continuous_tiles_count++;
567                 
568                 // This is set to true if the texture doesn't allow more tiling
569                 bool end_of_texture = false;
570                 /*
571                         If there is no texture, it can be tiled infinitely.
572                         If tiled==0, it means the texture can be tiled infinitely.
573                         Otherwise check tiled agains continuous_tiles_count.
574                 */
575                 if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
576                 {
577                         if(tile.texture.tiled <= continuous_tiles_count)
578                                 end_of_texture = true;
579                 }
580                 
581                 // Do this to disable tiling textures
582                 //end_of_texture = true; //DEBUG
583                 
584                 if(next_is_different || end_of_texture)
585                 {
586                         /*
587                                 Create a face if there should be one
588                         */
589                         if(makes_face)
590                         {
591                                 // Floating point conversion of the position vector
592                                 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
593                                 // Center point of face (kind of)
594                                 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
595                                 if(continuous_tiles_count != 1)
596                                         sp += translate_dir_f;
597                                 v3f scale(1,1,1);
598
599                                 if(translate_dir.X != 0)
600                                 {
601                                         scale.X = continuous_tiles_count;
602                                 }
603                                 if(translate_dir.Y != 0)
604                                 {
605                                         scale.Y = continuous_tiles_count;
606                                 }
607                                 if(translate_dir.Z != 0)
608                                 {
609                                         scale.Z = continuous_tiles_count;
610                                 }
611                                 
612                                 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
613                                                 sp, face_dir_corrected, scale,
614                                                 posRelative_f, dest);
615                                 
616                                 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
617                                 for(int i=1; i<continuous_tiles_count; i++){
618                                         g_profiler->avg("Meshgen: faces drawn by tiling", 1);
619                                 }
620                         }
621
622                         continuous_tiles_count = 0;
623                         
624                         makes_face = next_makes_face;
625                         p_corrected = next_p_corrected;
626                         face_dir_corrected = next_face_dir_corrected;
627                         lights[0] = next_lights[0];
628                         lights[1] = next_lights[1];
629                         lights[2] = next_lights[2];
630                         lights[3] = next_lights[3];
631                         tile = next_tile;
632                 }
633                 
634                 p = p_next;
635         }
636 }
637
638 scene::SMesh* makeMapBlockMesh(MeshMakeData *data)
639 {
640         // 4-21ms for MAP_BLOCKSIZE=16
641         // 24-155ms for MAP_BLOCKSIZE=32
642         //TimeTaker timer1("makeMapBlockMesh()");
643
644         core::array<FastFace> fastfaces_new;
645
646         v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
647         
648         // floating point conversion
649         v3f posRelative_f(blockpos_nodes.X, blockpos_nodes.Y, blockpos_nodes.Z);
650         
651         /*
652                 Some settings
653         */
654         //bool new_style_water = g_settings->getBool("new_style_water");
655         //bool new_style_leaves = g_settings->getBool("new_style_leaves");
656         bool smooth_lighting = g_settings->getBool("smooth_lighting");
657         
658         /*
659                 We are including the faces of the trailing edges of the block.
660                 This means that when something changes, the caller must
661                 also update the meshes of the blocks at the leading edges.
662
663                 NOTE: This is the slowest part of this method.
664         */
665         
666         {
667                 // 4-23ms for MAP_BLOCKSIZE=16
668                 //TimeTaker timer2("updateMesh() collect");
669
670                 /*
671                         Go through every y,z and get top(y+) faces in rows of x+
672                 */
673                 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
674                         for(s16 z=0; z<MAP_BLOCKSIZE; z++){
675                                 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
676                                                 v3s16(0,y,z), MAP_BLOCKSIZE,
677                                                 v3s16(1,0,0), //dir
678                                                 v3f  (1,0,0),
679                                                 v3s16(0,1,0), //face dir
680                                                 v3f  (0,1,0),
681                                                 fastfaces_new,
682                                                 data->m_temp_mods,
683                                                 data->m_vmanip,
684                                                 blockpos_nodes,
685                                                 smooth_lighting);
686                         }
687                 }
688                 /*
689                         Go through every x,y and get right(x+) faces in rows of z+
690                 */
691                 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
692                         for(s16 y=0; y<MAP_BLOCKSIZE; y++){
693                                 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
694                                                 v3s16(x,y,0), MAP_BLOCKSIZE,
695                                                 v3s16(0,0,1),
696                                                 v3f  (0,0,1),
697                                                 v3s16(1,0,0),
698                                                 v3f  (1,0,0),
699                                                 fastfaces_new,
700                                                 data->m_temp_mods,
701                                                 data->m_vmanip,
702                                                 blockpos_nodes,
703                                                 smooth_lighting);
704                         }
705                 }
706                 /*
707                         Go through every y,z and get back(z+) faces in rows of x+
708                 */
709                 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
710                         for(s16 y=0; y<MAP_BLOCKSIZE; y++){
711                                 updateFastFaceRow(data->m_daynight_ratio, posRelative_f,
712                                                 v3s16(0,y,z), MAP_BLOCKSIZE,
713                                                 v3s16(1,0,0),
714                                                 v3f  (1,0,0),
715                                                 v3s16(0,0,1),
716                                                 v3f  (0,0,1),
717                                                 fastfaces_new,
718                                                 data->m_temp_mods,
719                                                 data->m_vmanip,
720                                                 blockpos_nodes,
721                                                 smooth_lighting);
722                         }
723                 }
724         }
725
726         // End of slow part
727
728         /*
729                 Convert FastFaces to SMesh
730         */
731
732         MeshCollector collector;
733
734         if(fastfaces_new.size() > 0)
735         {
736                 // avg 0ms (100ms spikes when loading textures the first time)
737                 //TimeTaker timer2("updateMesh() mesh building");
738
739                 video::SMaterial material;
740                 material.setFlag(video::EMF_LIGHTING, false);
741                 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
742                 material.setFlag(video::EMF_BILINEAR_FILTER, false);
743                 material.setFlag(video::EMF_FOG_ENABLE, true);
744                 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
745                 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
746                 material.MaterialType
747                                 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
748
749                 for(u32 i=0; i<fastfaces_new.size(); i++)
750                 {
751                         FastFace &f = fastfaces_new[i];
752
753                         const u16 indices[] = {0,1,2,2,3,0};
754                         const u16 indices_alternate[] = {0,1,3,2,3,1};
755                         
756                         video::ITexture *texture = f.tile.texture.atlas;
757                         if(texture == NULL)
758                                 continue;
759
760                         material.setTexture(0, texture);
761                         
762                         f.tile.applyMaterialOptions(material);
763
764                         const u16 *indices_p = indices;
765                         
766                         /*
767                                 Revert triangles for nicer looking gradient if vertices
768                                 1 and 3 have same color or 0 and 2 have different color.
769                         */
770                         if(f.vertices[0].Color != f.vertices[2].Color
771                                         || f.vertices[1].Color == f.vertices[3].Color)
772                                 indices_p = indices_alternate;
773                         
774                         collector.append(material, f.vertices, 4, indices_p, 6);
775                 }
776         }
777
778         /*
779                 Add special graphics:
780                 - torches
781                 - flowing water
782                 - fences
783                 - whatever
784         */
785
786         mapblock_mesh_generate_special(data, collector);
787         
788         /*
789                 Add stuff from collector to mesh
790         */
791         
792         scene::SMesh *mesh_new = NULL;
793         mesh_new = new scene::SMesh();
794         
795         collector.fillMesh(mesh_new);
796
797         /*
798                 Do some stuff to the mesh
799         */
800
801         mesh_new->recalculateBoundingBox();
802
803         /*
804                 Delete new mesh if it is empty
805         */
806
807         if(mesh_new->getMeshBufferCount() == 0)
808         {
809                 mesh_new->drop();
810                 mesh_new = NULL;
811         }
812
813         if(mesh_new)
814         {
815 #if 0
816                 // Usually 1-700 faces and 1-7 materials
817                 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
818                                 <<"and uses "<<mesh_new->getMeshBufferCount()
819                                 <<" materials (meshbuffers)"<<std::endl;
820 #endif
821
822                 // Use VBO for mesh (this just would set this for ever buffer)
823                 // This will lead to infinite memory usage because or irrlicht.
824                 //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
825
826                 /*
827                         NOTE: If that is enabled, some kind of a queue to the main
828                         thread should be made which would call irrlicht to delete
829                         the hardware buffer and then delete the mesh
830                 */
831         }
832
833         return mesh_new;
834         
835         //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
836 }
837