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