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