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