Properly and efficiently use split utility headers
[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 Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser 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_profiler
25 #include "profiler.h"
26 #include "nodedef.h"
27 #include "gamedef.h"
28 #include "mesh.h"
29 #include "content_mapblock.h"
30 #include "noise.h"
31 #include "settings.h"
32 #include "util/directiontables.h"
33
34 /*
35         MeshMakeData
36 */
37
38 MeshMakeData::MeshMakeData(IGameDef *gamedef):
39         m_vmanip(),
40         m_blockpos(-1337,-1337,-1337),
41         m_crack_pos_relative(-1337, -1337, -1337),
42         m_smooth_lighting(false),
43         m_gamedef(gamedef)
44 {}
45
46 void MeshMakeData::fill(MapBlock *block)
47 {
48         m_blockpos = block->getPos();
49
50         v3s16 blockpos_nodes = m_blockpos*MAP_BLOCKSIZE;
51         
52         /*
53                 Copy data
54         */
55
56         // Allocate this block + neighbors
57         m_vmanip.clear();
58         m_vmanip.addArea(VoxelArea(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
59                         blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)));
60
61         {
62                 //TimeTaker timer("copy central block data");
63                 // 0ms
64
65                 // Copy our data
66                 block->copyTo(m_vmanip);
67         }
68         {
69                 //TimeTaker timer("copy neighbor block data");
70                 // 0ms
71
72                 /*
73                         Copy neighbors. This is lightning fast.
74                         Copying only the borders would be *very* slow.
75                 */
76                 
77                 // Get map
78                 Map *map = block->getParent();
79
80                 for(u16 i=0; i<6; i++)
81                 {
82                         const v3s16 &dir = g_6dirs[i];
83                         v3s16 bp = m_blockpos + dir;
84                         MapBlock *b = map->getBlockNoCreateNoEx(bp);
85                         if(b)
86                                 b->copyTo(m_vmanip);
87                 }
88         }
89 }
90
91 void MeshMakeData::fillSingleNode(MapNode *node)
92 {
93         m_blockpos = v3s16(0,0,0);
94         
95         v3s16 blockpos_nodes = v3s16(0,0,0);
96         VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE,
97                         blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1));
98         s32 volume = area.getVolume();
99         s32 our_node_index = area.index(1,1,1);
100
101         // Allocate this block + neighbors
102         m_vmanip.clear();
103         m_vmanip.addArea(area);
104
105         // Fill in data
106         MapNode *data = new MapNode[volume];
107         for(s32 i = 0; i < volume; i++)
108         {
109                 if(i == our_node_index)
110                 {
111                         data[i] = *node;
112                 }
113                 else
114                 {
115                         data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0);
116                 }
117         }
118         m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent());
119         delete[] data;
120 }
121
122 void MeshMakeData::setCrack(int crack_level, v3s16 crack_pos)
123 {
124         if(crack_level >= 0)
125                 m_crack_pos_relative = crack_pos - m_blockpos*MAP_BLOCKSIZE;
126 }
127
128 void MeshMakeData::setSmoothLighting(bool smooth_lighting)
129 {
130         m_smooth_lighting = smooth_lighting;
131 }
132
133 /*
134         Light and vertex color functions
135 */
136
137 /*
138         Calculate non-smooth lighting at interior of node.
139         Single light bank.
140 */
141 static u8 getInteriorLight(enum LightBank bank, MapNode n, s32 increment,
142                 MeshMakeData *data)
143 {
144         INodeDefManager *ndef = data->m_gamedef->ndef();
145         u8 light = n.getLight(bank, ndef);
146
147         while(increment > 0)
148         {
149                 light = undiminish_light(light);
150                 --increment;
151         }
152         while(increment < 0)
153         {
154                 light = diminish_light(light);
155                 ++increment;
156         }
157
158         return decode_light(light);
159 }
160
161 /*
162         Calculate non-smooth lighting at interior of node.
163         Both light banks.
164 */
165 u16 getInteriorLight(MapNode n, s32 increment, MeshMakeData *data)
166 {
167         u16 day = getInteriorLight(LIGHTBANK_DAY, n, increment, data);
168         u16 night = getInteriorLight(LIGHTBANK_NIGHT, n, increment, data);
169         return day | (night << 8);
170 }
171
172 /*
173         Calculate non-smooth lighting at face of node.
174         Single light bank.
175 */
176 static u8 getFaceLight(enum LightBank bank, MapNode n, MapNode n2,
177                 v3s16 face_dir, MeshMakeData *data)
178 {
179         INodeDefManager *ndef = data->m_gamedef->ndef();
180
181         u8 light;
182         u8 l1 = n.getLight(bank, ndef);
183         u8 l2 = n2.getLight(bank, ndef);
184         if(l1 > l2)
185                 light = l1;
186         else
187                 light = l2;
188
189         // Boost light level for light sources
190         u8 light_source = MYMAX(ndef->get(n).light_source,
191                         ndef->get(n2).light_source);
192         //if(light_source >= light)
193                 //return decode_light(undiminish_light(light_source));
194         if(light_source > light)
195                 //return decode_light(light_source);
196                 light = light_source;
197
198         // Make some nice difference to different sides
199
200         // This makes light come from a corner
201         /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
202                 light = diminish_light(diminish_light(light));
203         else if(face_dir.X == -1 || face_dir.Z == -1)
204                 light = diminish_light(light);*/
205
206         // All neighboring faces have different shade (like in minecraft)
207         if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
208                 light = diminish_light(diminish_light(light));
209         else if(face_dir.Z == 1 || face_dir.Z == -1)
210                 light = diminish_light(light);
211
212         return decode_light(light);
213 }
214
215 /*
216         Calculate non-smooth lighting at face of node.
217         Both light banks.
218 */
219 u16 getFaceLight(MapNode n, MapNode n2, v3s16 face_dir, MeshMakeData *data)
220 {
221         u16 day = getFaceLight(LIGHTBANK_DAY, n, n2, face_dir, data);
222         u16 night = getFaceLight(LIGHTBANK_NIGHT, n, n2, face_dir, data);
223         return day | (night << 8);
224 }
225
226 /*
227         Calculate smooth lighting at the XYZ- corner of p.
228         Single light bank.
229 */
230 static u8 getSmoothLight(enum LightBank bank, v3s16 p, MeshMakeData *data)
231 {
232         static v3s16 dirs8[8] = {
233                 v3s16(0,0,0),
234                 v3s16(0,0,1),
235                 v3s16(0,1,0),
236                 v3s16(0,1,1),
237                 v3s16(1,0,0),
238                 v3s16(1,1,0),
239                 v3s16(1,0,1),
240                 v3s16(1,1,1),
241         };
242
243         INodeDefManager *ndef = data->m_gamedef->ndef();
244
245         u16 ambient_occlusion = 0;
246         u16 light = 0;
247         u16 light_count = 0;
248         u8 light_source_max = 0;
249         for(u32 i=0; i<8; i++)
250         {
251                 MapNode n = data->m_vmanip.getNodeNoEx(p - dirs8[i]);
252                 const ContentFeatures &f = ndef->get(n);
253                 if(f.light_source > light_source_max)
254                         light_source_max = f.light_source;
255                 // Check f.solidness because fast-style leaves look
256                 // better this way
257                 if(f.param_type == CPT_LIGHT && f.solidness != 2)
258                 {
259                         light += decode_light(n.getLight(bank, ndef));
260                         light_count++;
261                 }
262                 else if(n.getContent() != CONTENT_IGNORE)
263                 {
264                         ambient_occlusion++;
265                 }
266         }
267
268         if(light_count == 0)
269                 return 255;
270         
271         light /= light_count;
272
273         // Boost brightness around light sources
274         if(decode_light(light_source_max) >= light)
275                 //return decode_light(undiminish_light(light_source_max));
276                 return decode_light(light_source_max);
277
278         if(ambient_occlusion > 4)
279         {
280                 //ambient_occlusion -= 4;
281                 //light = (float)light / ((float)ambient_occlusion * 0.5 + 1.0);
282                 float light_amount = (8 - ambient_occlusion) / 4.0;
283                 float light_f = (float)light / 255.0;
284                 light_f = pow(light_f, 2.2f); // gamma -> linear space
285                 light_f = light_f * light_amount;
286                 light_f = pow(light_f, 1.0f/2.2f); // linear -> gamma space
287                 if(light_f > 1.0)
288                         light_f = 1.0;
289                 light = 255.0 * light_f + 0.5;
290         }
291
292         return light;
293 }
294
295 /*
296         Calculate smooth lighting at the XYZ- corner of p.
297         Both light banks.
298 */
299 static u16 getSmoothLight(v3s16 p, MeshMakeData *data)
300 {
301         u16 day = getSmoothLight(LIGHTBANK_DAY, p, data);
302         u16 night = getSmoothLight(LIGHTBANK_NIGHT, p, data);
303         return day | (night << 8);
304 }
305
306 /*
307         Calculate smooth lighting at the given corner of p.
308         Both light banks.
309 */
310 u16 getSmoothLight(v3s16 p, v3s16 corner, MeshMakeData *data)
311 {
312         if(corner.X == 1) p.X += 1;
313         else              assert(corner.X == -1);
314         if(corner.Y == 1) p.Y += 1;
315         else              assert(corner.Y == -1);
316         if(corner.Z == 1) p.Z += 1;
317         else              assert(corner.Z == -1);
318         
319         return getSmoothLight(p, data);
320 }
321
322 /*
323         Converts from day + night color values (0..255)
324         and a given daynight_ratio to the final SColor shown on screen.
325 */
326 static void finalColorBlend(video::SColor& result,
327                 u8 day, u8 night, u32 daynight_ratio)
328 {
329         s32 rg = (day * daynight_ratio + night * (1000-daynight_ratio)) / 1000;
330         s32 b = rg;
331
332         // Moonlight is blue
333         b += (day - night) / 13;
334         rg -= (day - night) / 23;
335
336         // Emphase blue a bit in darker places
337         // Each entry of this array represents a range of 8 blue levels
338         static u8 emphase_blue_when_dark[32] = {
339                 1, 4, 6, 6, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0,
340                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
341         };
342         if(b < 0)
343                 b = 0;
344         if(b > 255)
345                 b = 255;
346         b += emphase_blue_when_dark[b / 8];
347
348         // Artificial light is yellow-ish
349         static u8 emphase_yellow_when_artificial[16] = {
350                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 15, 15, 15
351         };
352         rg += emphase_yellow_when_artificial[night/16];
353         if(rg < 0)
354                 rg = 0;
355         if(rg > 255)
356                 rg = 255;
357
358         result.setRed(rg);
359         result.setGreen(rg);
360         result.setBlue(b);
361 }
362
363 /*
364         Mesh generation helpers
365 */
366
367 /*
368         vertex_dirs: v3s16[4]
369 */
370 static void getNodeVertexDirs(v3s16 dir, v3s16 *vertex_dirs)
371 {
372         /*
373                 If looked from outside the node towards the face, the corners are:
374                 0: bottom-right
375                 1: bottom-left
376                 2: top-left
377                 3: top-right
378         */
379         if(dir == v3s16(0,0,1))
380         {
381                 // If looking towards z+, this is the face that is behind
382                 // the center point, facing towards z+.
383                 vertex_dirs[0] = v3s16(-1,-1, 1);
384                 vertex_dirs[1] = v3s16( 1,-1, 1);
385                 vertex_dirs[2] = v3s16( 1, 1, 1);
386                 vertex_dirs[3] = v3s16(-1, 1, 1);
387         }
388         else if(dir == v3s16(0,0,-1))
389         {
390                 // faces towards Z-
391                 vertex_dirs[0] = v3s16( 1,-1,-1);
392                 vertex_dirs[1] = v3s16(-1,-1,-1);
393                 vertex_dirs[2] = v3s16(-1, 1,-1);
394                 vertex_dirs[3] = v3s16( 1, 1,-1);
395         }
396         else if(dir == v3s16(1,0,0))
397         {
398                 // faces towards X+
399                 vertex_dirs[0] = v3s16( 1,-1, 1);
400                 vertex_dirs[1] = v3s16( 1,-1,-1);
401                 vertex_dirs[2] = v3s16( 1, 1,-1);
402                 vertex_dirs[3] = v3s16( 1, 1, 1);
403         }
404         else if(dir == v3s16(-1,0,0))
405         {
406                 // faces towards X-
407                 vertex_dirs[0] = v3s16(-1,-1,-1);
408                 vertex_dirs[1] = v3s16(-1,-1, 1);
409                 vertex_dirs[2] = v3s16(-1, 1, 1);
410                 vertex_dirs[3] = v3s16(-1, 1,-1);
411         }
412         else if(dir == v3s16(0,1,0))
413         {
414                 // faces towards Y+ (assume Z- as "down" in texture)
415                 vertex_dirs[0] = v3s16( 1, 1,-1);
416                 vertex_dirs[1] = v3s16(-1, 1,-1);
417                 vertex_dirs[2] = v3s16(-1, 1, 1);
418                 vertex_dirs[3] = v3s16( 1, 1, 1);
419         }
420         else if(dir == v3s16(0,-1,0))
421         {
422                 // faces towards Y- (assume Z+ as "down" in texture)
423                 vertex_dirs[0] = v3s16( 1,-1, 1);
424                 vertex_dirs[1] = v3s16(-1,-1, 1);
425                 vertex_dirs[2] = v3s16(-1,-1,-1);
426                 vertex_dirs[3] = v3s16( 1,-1,-1);
427         }
428 }
429
430 struct FastFace
431 {
432         TileSpec tile;
433         video::S3DVertex vertices[4]; // Precalculated vertices
434 };
435
436 static void makeFastFace(TileSpec tile, u16 li0, u16 li1, u16 li2, u16 li3,
437                 v3f p, v3s16 dir, v3f scale, core::array<FastFace> &dest)
438 {
439         FastFace face;
440         
441         // Position is at the center of the cube.
442         v3f pos = p * BS;
443
444         v3f vertex_pos[4];
445         v3s16 vertex_dirs[4];
446         getNodeVertexDirs(dir, vertex_dirs);
447         for(u16 i=0; i<4; i++)
448         {
449                 vertex_pos[i] = v3f(
450                                 BS/2*vertex_dirs[i].X,
451                                 BS/2*vertex_dirs[i].Y,
452                                 BS/2*vertex_dirs[i].Z
453                 );
454         }
455
456         for(u16 i=0; i<4; i++)
457         {
458                 vertex_pos[i].X *= scale.X;
459                 vertex_pos[i].Y *= scale.Y;
460                 vertex_pos[i].Z *= scale.Z;
461                 vertex_pos[i] += pos;
462         }
463
464         f32 abs_scale = 1.;
465         if     (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
466         else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
467         else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
468
469         v3f normal(dir.X, dir.Y, dir.Z);
470
471         u8 alpha = tile.alpha;
472
473         float x0 = tile.texture.pos.X;
474         float y0 = tile.texture.pos.Y;
475         float w = tile.texture.size.X;
476         float h = tile.texture.size.Y;
477
478         face.vertices[0] = video::S3DVertex(vertex_pos[0], normal,
479                         MapBlock_LightColor(alpha, li0),
480                         core::vector2d<f32>(x0+w*abs_scale, y0+h));
481         face.vertices[1] = video::S3DVertex(vertex_pos[1], normal,
482                         MapBlock_LightColor(alpha, li1),
483                         core::vector2d<f32>(x0, y0+h));
484         face.vertices[2] = video::S3DVertex(vertex_pos[2], normal,
485                         MapBlock_LightColor(alpha, li2),
486                         core::vector2d<f32>(x0, y0));
487         face.vertices[3] = video::S3DVertex(vertex_pos[3], normal,
488                         MapBlock_LightColor(alpha, li3),
489                         core::vector2d<f32>(x0+w*abs_scale, y0));
490
491         face.tile = tile;
492         
493         dest.push_back(face);
494 }
495
496 /*
497         Nodes make a face if contents differ and solidness differs.
498         Return value:
499                 0: No face
500                 1: Face uses m1's content
501                 2: Face uses m2's content
502         equivalent: Whether the blocks share the same face (eg. water and glass)
503
504         TODO: Add 3: Both faces drawn with backface culling, remove equivalent
505 */
506 static u8 face_contents(content_t m1, content_t m2, bool *equivalent,
507                 INodeDefManager *ndef)
508 {
509         *equivalent = false;
510
511         if(m1 == CONTENT_IGNORE || m2 == CONTENT_IGNORE)
512                 return 0;
513         
514         bool contents_differ = (m1 != m2);
515         
516         const ContentFeatures &f1 = ndef->get(m1);
517         const ContentFeatures &f2 = ndef->get(m2);
518
519         // Contents don't differ for different forms of same liquid
520         if(f1.sameLiquid(f2))
521                 contents_differ = false;
522         
523         u8 c1 = f1.solidness;
524         u8 c2 = f2.solidness;
525
526         bool solidness_differs = (c1 != c2);
527         bool makes_face = contents_differ && solidness_differs;
528
529         if(makes_face == false)
530                 return 0;
531         
532         if(c1 == 0)
533                 c1 = f1.visual_solidness;
534         if(c2 == 0)
535                 c2 = f2.visual_solidness;
536         
537         if(c1 == c2){
538                 *equivalent = true;
539                 // If same solidness, liquid takes precense
540                 if(f1.isLiquid())
541                         return 1;
542                 if(f2.isLiquid())
543                         return 2;
544         }
545         
546         if(c1 > c2)
547                 return 1;
548         else
549                 return 2;
550 }
551
552 /*
553         Gets nth node tile (0 <= n <= 5).
554 */
555 TileSpec getNodeTileN(MapNode mn, v3s16 p, u8 tileindex, MeshMakeData *data)
556 {
557         INodeDefManager *ndef = data->m_gamedef->ndef();
558         TileSpec spec = ndef->get(mn).tiles[tileindex];
559         // Apply temporary crack
560         if(p == data->m_crack_pos_relative)
561         {
562                 spec.material_flags |= MATERIAL_FLAG_CRACK;
563                 spec.texture = data->m_gamedef->tsrc()->getTextureRawAP(spec.texture);
564         }
565         // If animated, replace tile texture with one without texture atlas
566         if(spec.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
567         {
568                 spec.texture = data->m_gamedef->tsrc()->getTextureRawAP(spec.texture);
569         }
570         return spec;
571 }
572
573 /*
574         Gets node tile given a face direction.
575 */
576 TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 dir, MeshMakeData *data)
577 {
578         INodeDefManager *ndef = data->m_gamedef->ndef();
579
580         // Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
581         // (0,0,1), (0,0,-1) or (0,0,0)
582         assert(dir.X * dir.X + dir.Y * dir.Y + dir.Z * dir.Z <= 1);
583
584         // Convert direction to single integer for table lookup
585         //  0 = (0,0,0)
586         //  1 = (1,0,0)
587         //  2 = (0,1,0)
588         //  3 = (0,0,1)
589         //  4 = invalid, treat as (0,0,0)
590         //  5 = (0,0,-1)
591         //  6 = (0,-1,0)
592         //  7 = (-1,0,0)
593         u8 dir_i = (dir.X + 2 * dir.Y + 3 * dir.Z) & 7;
594
595         // Get rotation for things like chests
596         u8 facedir = mn.getFaceDir(ndef);
597         assert(facedir <= 3);
598         
599         static const u8 dir_to_tile[4 * 8] =
600         {
601                 // 0  +X  +Y  +Z   0  -Z  -Y  -X
602                    0,  2,  0,  4,  0,  5,  1,  3,  // facedir = 0
603                    0,  4,  0,  3,  0,  2,  1,  5,  // facedir = 1
604                    0,  3,  0,  5,  0,  4,  1,  2,  // facedir = 2
605                    0,  5,  0,  2,  0,  3,  1,  4,  // facedir = 3
606         };
607         u8 tileindex = dir_to_tile[facedir*8 + dir_i];
608         return getNodeTileN(mn, p, tileindex, data);
609 }
610
611 static void getTileInfo(
612                 // Input:
613                 MeshMakeData *data,
614                 v3s16 p,
615                 v3s16 face_dir,
616                 // Output:
617                 bool &makes_face,
618                 v3s16 &p_corrected,
619                 v3s16 &face_dir_corrected,
620                 u16 *lights,
621                 TileSpec &tile
622         )
623 {
624         VoxelManipulator &vmanip = data->m_vmanip;
625         INodeDefManager *ndef = data->m_gamedef->ndef();
626         v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
627
628         MapNode n0 = vmanip.getNodeNoEx(blockpos_nodes + p);
629         MapNode n1 = vmanip.getNodeNoEx(blockpos_nodes + p + face_dir);
630         TileSpec tile0 = getNodeTile(n0, p, face_dir, data);
631         TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, data);
632         
633         // This is hackish
634         bool equivalent = false;
635         u8 mf = face_contents(n0.getContent(), n1.getContent(),
636                         &equivalent, ndef);
637
638         if(mf == 0)
639         {
640                 makes_face = false;
641                 return;
642         }
643
644         makes_face = true;
645         
646         if(mf == 1)
647         {
648                 tile = tile0;
649                 p_corrected = p;
650                 face_dir_corrected = face_dir;
651         }
652         else
653         {
654                 tile = tile1;
655                 p_corrected = p + face_dir;
656                 face_dir_corrected = -face_dir;
657         }
658         
659         // eg. water and glass
660         if(equivalent)
661                 tile.material_flags |= MATERIAL_FLAG_BACKFACE_CULLING;
662         
663         if(data->m_smooth_lighting == false)
664         {
665                 lights[0] = lights[1] = lights[2] = lights[3] =
666                                 getFaceLight(n0, n1, face_dir, data);
667         }
668         else
669         {
670                 v3s16 vertex_dirs[4];
671                 getNodeVertexDirs(face_dir_corrected, vertex_dirs);
672                 for(u16 i=0; i<4; i++)
673                 {
674                         lights[i] = getSmoothLight(
675                                         blockpos_nodes + p_corrected,
676                                         vertex_dirs[i], data);
677                 }
678         }
679         
680         return;
681 }
682
683 /*
684         startpos:
685         translate_dir: unit vector with only one of x, y or z
686         face_dir: unit vector with only one of x, y or z
687 */
688 static void updateFastFaceRow(
689                 MeshMakeData *data,
690                 v3s16 startpos,
691                 v3s16 translate_dir,
692                 v3f translate_dir_f,
693                 v3s16 face_dir,
694                 v3f face_dir_f,
695                 core::array<FastFace> &dest)
696 {
697         v3s16 p = startpos;
698         
699         u16 continuous_tiles_count = 0;
700         
701         bool makes_face = false;
702         v3s16 p_corrected;
703         v3s16 face_dir_corrected;
704         u16 lights[4] = {0,0,0,0};
705         TileSpec tile;
706         getTileInfo(data, p, face_dir, 
707                         makes_face, p_corrected, face_dir_corrected,
708                         lights, tile);
709
710         for(u16 j=0; j<MAP_BLOCKSIZE; j++)
711         {
712                 // If tiling can be done, this is set to false in the next step
713                 bool next_is_different = true;
714                 
715                 v3s16 p_next;
716                 
717                 bool next_makes_face = false;
718                 v3s16 next_p_corrected;
719                 v3s16 next_face_dir_corrected;
720                 u16 next_lights[4] = {0,0,0,0};
721                 TileSpec next_tile;
722                 
723                 // If at last position, there is nothing to compare to and
724                 // the face must be drawn anyway
725                 if(j != MAP_BLOCKSIZE - 1)
726                 {
727                         p_next = p + translate_dir;
728                         
729                         getTileInfo(data, p_next, face_dir,
730                                         next_makes_face, next_p_corrected,
731                                         next_face_dir_corrected, next_lights,
732                                         next_tile);
733                         
734                         if(next_makes_face == makes_face
735                                         && next_p_corrected == p_corrected + translate_dir
736                                         && next_face_dir_corrected == face_dir_corrected
737                                         && next_lights[0] == lights[0]
738                                         && next_lights[1] == lights[1]
739                                         && next_lights[2] == lights[2]
740                                         && next_lights[3] == lights[3]
741                                         && next_tile == tile)
742                         {
743                                 next_is_different = false;
744                         }
745                         else{
746                                 /*if(makes_face){
747                                         g_profiler->add("Meshgen: diff: next_makes_face != makes_face",
748                                                         next_makes_face != makes_face ? 1 : 0);
749                                         g_profiler->add("Meshgen: diff: n_p_corr != p_corr + t_dir",
750                                                         (next_p_corrected != p_corrected + translate_dir) ? 1 : 0);
751                                         g_profiler->add("Meshgen: diff: next_f_dir_corr != f_dir_corr",
752                                                         next_face_dir_corrected != face_dir_corrected ? 1 : 0);
753                                         g_profiler->add("Meshgen: diff: next_lights[] != lights[]",
754                                                         (next_lights[0] != lights[0] ||
755                                                         next_lights[0] != lights[0] ||
756                                                         next_lights[0] != lights[0] ||
757                                                         next_lights[0] != lights[0]) ? 1 : 0);
758                                         g_profiler->add("Meshgen: diff: !(next_tile == tile)",
759                                                         !(next_tile == tile) ? 1 : 0);
760                                 }*/
761                         }
762                         /*g_profiler->add("Meshgen: Total faces checked", 1);
763                         if(makes_face)
764                                 g_profiler->add("Meshgen: Total makes_face checked", 1);*/
765                 } else {
766                         /*if(makes_face)
767                                 g_profiler->add("Meshgen: diff: last position", 1);*/
768                 }
769
770                 continuous_tiles_count++;
771                 
772                 // This is set to true if the texture doesn't allow more tiling
773                 bool end_of_texture = false;
774                 /*
775                         If there is no texture, it can be tiled infinitely.
776                         If tiled==0, it means the texture can be tiled infinitely.
777                         Otherwise check tiled agains continuous_tiles_count.
778                 */
779                 if(tile.texture.atlas != NULL && tile.texture.tiled != 0)
780                 {
781                         if(tile.texture.tiled <= continuous_tiles_count)
782                                 end_of_texture = true;
783                 }
784                 
785                 // Do this to disable tiling textures
786                 //end_of_texture = true; //DEBUG
787                 
788                 if(next_is_different || end_of_texture)
789                 {
790                         /*
791                                 Create a face if there should be one
792                         */
793                         if(makes_face)
794                         {
795                                 // Floating point conversion of the position vector
796                                 v3f pf(p_corrected.X, p_corrected.Y, p_corrected.Z);
797                                 // Center point of face (kind of)
798                                 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
799                                 if(continuous_tiles_count != 1)
800                                         sp += translate_dir_f;
801                                 v3f scale(1,1,1);
802
803                                 if(translate_dir.X != 0)
804                                 {
805                                         scale.X = continuous_tiles_count;
806                                 }
807                                 if(translate_dir.Y != 0)
808                                 {
809                                         scale.Y = continuous_tiles_count;
810                                 }
811                                 if(translate_dir.Z != 0)
812                                 {
813                                         scale.Z = continuous_tiles_count;
814                                 }
815                                 
816                                 makeFastFace(tile, lights[0], lights[1], lights[2], lights[3],
817                                                 sp, face_dir_corrected, scale,
818                                                 dest);
819                                 
820                                 g_profiler->avg("Meshgen: faces drawn by tiling", 0);
821                                 for(int i=1; i<continuous_tiles_count; i++){
822                                         g_profiler->avg("Meshgen: faces drawn by tiling", 1);
823                                 }
824                         }
825
826                         continuous_tiles_count = 0;
827                         
828                         makes_face = next_makes_face;
829                         p_corrected = next_p_corrected;
830                         face_dir_corrected = next_face_dir_corrected;
831                         lights[0] = next_lights[0];
832                         lights[1] = next_lights[1];
833                         lights[2] = next_lights[2];
834                         lights[3] = next_lights[3];
835                         tile = next_tile;
836                 }
837                 
838                 p = p_next;
839         }
840 }
841
842 static void updateAllFastFaceRows(MeshMakeData *data,
843                 core::array<FastFace> &dest)
844 {
845         /*
846                 Go through every y,z and get top(y+) faces in rows of x+
847         */
848         for(s16 y=0; y<MAP_BLOCKSIZE; y++){
849                 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
850                         updateFastFaceRow(data,
851                                         v3s16(0,y,z),
852                                         v3s16(1,0,0), //dir
853                                         v3f  (1,0,0),
854                                         v3s16(0,1,0), //face dir
855                                         v3f  (0,1,0),
856                                         dest);
857                 }
858         }
859
860         /*
861                 Go through every x,y and get right(x+) faces in rows of z+
862         */
863         for(s16 x=0; x<MAP_BLOCKSIZE; x++){
864                 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
865                         updateFastFaceRow(data,
866                                         v3s16(x,y,0),
867                                         v3s16(0,0,1), //dir
868                                         v3f  (0,0,1),
869                                         v3s16(1,0,0), //face dir
870                                         v3f  (1,0,0),
871                                         dest);
872                 }
873         }
874
875         /*
876                 Go through every y,z and get back(z+) faces in rows of x+
877         */
878         for(s16 z=0; z<MAP_BLOCKSIZE; z++){
879                 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
880                         updateFastFaceRow(data,
881                                         v3s16(0,y,z),
882                                         v3s16(1,0,0), //dir
883                                         v3f  (1,0,0),
884                                         v3s16(0,0,1), //face dir
885                                         v3f  (0,0,1),
886                                         dest);
887                 }
888         }
889 }
890
891 /*
892         MapBlockMesh
893 */
894
895 MapBlockMesh::MapBlockMesh(MeshMakeData *data):
896         m_mesh(new scene::SMesh()),
897         m_gamedef(data->m_gamedef),
898         m_animation_force_timer(0), // force initial animation
899         m_last_crack(-1),
900         m_crack_materials(),
901         m_last_daynight_ratio((u32) -1),
902         m_daynight_diffs()
903 {
904         // 4-21ms for MAP_BLOCKSIZE=16  (NOTE: probably outdated)
905         // 24-155ms for MAP_BLOCKSIZE=32  (NOTE: probably outdated)
906         //TimeTaker timer1("MapBlockMesh()");
907
908         core::array<FastFace> fastfaces_new;
909
910         /*
911                 We are including the faces of the trailing edges of the block.
912                 This means that when something changes, the caller must
913                 also update the meshes of the blocks at the leading edges.
914
915                 NOTE: This is the slowest part of this method.
916         */
917         {
918                 // 4-23ms for MAP_BLOCKSIZE=16  (NOTE: probably outdated)
919                 //TimeTaker timer2("updateAllFastFaceRows()");
920                 updateAllFastFaceRows(data, fastfaces_new);
921         }
922         // End of slow part
923
924         /*
925                 Convert FastFaces to MeshCollector
926         */
927
928         MeshCollector collector;
929
930         {
931                 // avg 0ms (100ms spikes when loading textures the first time)
932                 // (NOTE: probably outdated)
933                 //TimeTaker timer2("MeshCollector building");
934
935                 for(u32 i=0; i<fastfaces_new.size(); i++)
936                 {
937                         FastFace &f = fastfaces_new[i];
938
939                         const u16 indices[] = {0,1,2,2,3,0};
940                         const u16 indices_alternate[] = {0,1,3,2,3,1};
941                         
942                         if(f.tile.texture.atlas == NULL)
943                                 continue;
944
945                         const u16 *indices_p = indices;
946                         
947                         /*
948                                 Revert triangles for nicer looking gradient if vertices
949                                 1 and 3 have same color or 0 and 2 have different color.
950                                 getRed() is the day color.
951                         */
952                         if(f.vertices[0].Color.getRed() != f.vertices[2].Color.getRed()
953                                         || f.vertices[1].Color.getRed() == f.vertices[3].Color.getRed())
954                                 indices_p = indices_alternate;
955                         
956                         collector.append(f.tile, f.vertices, 4, indices_p, 6);
957                 }
958         }
959
960         /*
961                 Add special graphics:
962                 - torches
963                 - flowing water
964                 - fences
965                 - whatever
966         */
967
968         mapblock_mesh_generate_special(data, collector);
969         
970
971         /*
972                 Convert MeshCollector to SMesh
973                 Also store animation info
974         */
975         for(u32 i = 0; i < collector.prebuffers.size(); i++)
976         {
977                 PreMeshBuffer &p = collector.prebuffers[i];
978                 /*dstream<<"p.vertices.size()="<<p.vertices.size()
979                                 <<", p.indices.size()="<<p.indices.size()
980                                 <<std::endl;*/
981
982                 // Generate animation data
983                 // - Cracks
984                 if(p.tile.material_flags & MATERIAL_FLAG_CRACK)
985                 {
986                         ITextureSource *tsrc = data->m_gamedef->tsrc();
987                         std::string crack_basename = tsrc->getTextureName(p.tile.texture.id);
988                         if(p.tile.material_flags & MATERIAL_FLAG_CRACK_OVERLAY)
989                                 crack_basename += "^[cracko";
990                         else
991                                 crack_basename += "^[crack";
992                         m_crack_materials.insert(std::make_pair(i, crack_basename));
993                 }
994                 // - Texture animation
995                 if(p.tile.material_flags & MATERIAL_FLAG_ANIMATION_VERTICAL_FRAMES)
996                 {
997                         ITextureSource *tsrc = data->m_gamedef->tsrc();
998                         // Add to MapBlockMesh in order to animate these tiles
999                         m_animation_tiles[i] = p.tile;
1000                         m_animation_frames[i] = 0;
1001                         if(g_settings->getBool("desynchronize_mapblock_texture_animation")){
1002                                 // Get starting position from noise
1003                                 m_animation_frame_offsets[i] = 100000 * (2.0 + noise3d(
1004                                                 data->m_blockpos.X, data->m_blockpos.Y,
1005                                                 data->m_blockpos.Z, 0));
1006                         } else {
1007                                 // Play all synchronized
1008                                 m_animation_frame_offsets[i] = 0;
1009                         }
1010                         // Replace tile texture with the first animation frame
1011                         std::ostringstream os(std::ios::binary);
1012                         os<<tsrc->getTextureName(p.tile.texture.id);
1013                         os<<"^[verticalframe:"<<(int)p.tile.animation_frame_count<<":0";
1014                         p.tile.texture = tsrc->getTexture(os.str());
1015                 }
1016                 // - Lighting
1017                 for(u32 j = 0; j < p.vertices.size(); j++)
1018                 {
1019                         video::SColor &vc = p.vertices[j].Color;
1020                         u8 day = vc.getRed();
1021                         u8 night = vc.getGreen();
1022                         finalColorBlend(vc, day, night, 1000);
1023                         if(day != night)
1024                                 m_daynight_diffs[i][j] = std::make_pair(day, night);
1025                 }
1026
1027
1028                 // Create material
1029                 video::SMaterial material;
1030                 material.setFlag(video::EMF_LIGHTING, false);
1031                 material.setFlag(video::EMF_BACK_FACE_CULLING, true);
1032                 material.setFlag(video::EMF_BILINEAR_FILTER, false);
1033                 material.setFlag(video::EMF_FOG_ENABLE, true);
1034                 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
1035                 //material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_SIMPLE);
1036                 material.MaterialType
1037                                 = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1038                 material.setTexture(0, p.tile.texture.atlas);
1039                 p.tile.applyMaterialOptions(material);
1040
1041                 // Create meshbuffer
1042
1043                 // This is a "Standard MeshBuffer",
1044                 // it's a typedeffed CMeshBuffer<video::S3DVertex>
1045                 scene::SMeshBuffer *buf = new scene::SMeshBuffer();
1046                 // Set material
1047                 buf->Material = material;
1048                 // Add to mesh
1049                 m_mesh->addMeshBuffer(buf);
1050                 // Mesh grabbed it
1051                 buf->drop();
1052                 buf->append(p.vertices.pointer(), p.vertices.size(),
1053                                 p.indices.pointer(), p.indices.size());
1054         }
1055
1056         /*
1057                 Do some stuff to the mesh
1058         */
1059
1060         translateMesh(m_mesh, intToFloat(data->m_blockpos * MAP_BLOCKSIZE, BS));
1061         m_mesh->recalculateBoundingBox(); // translateMesh already does this
1062
1063         if(m_mesh)
1064         {
1065 #if 0
1066                 // Usually 1-700 faces and 1-7 materials
1067                 std::cout<<"Updated MapBlock has "<<fastfaces_new.size()<<" faces "
1068                                 <<"and uses "<<m_mesh->getMeshBufferCount()
1069                                 <<" materials (meshbuffers)"<<std::endl;
1070 #endif
1071
1072                 // Use VBO for mesh (this just would set this for ever buffer)
1073                 // This will lead to infinite memory usage because or irrlicht.
1074                 //m_mesh->setHardwareMappingHint(scene::EHM_STATIC);
1075
1076                 /*
1077                         NOTE: If that is enabled, some kind of a queue to the main
1078                         thread should be made which would call irrlicht to delete
1079                         the hardware buffer and then delete the mesh
1080                 */
1081         }
1082         
1083         //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1084
1085         // Check if animation is required for this mesh
1086         m_has_animation =
1087                 !m_crack_materials.empty() ||
1088                 !m_daynight_diffs.empty() ||
1089                 !m_animation_tiles.empty();
1090 }
1091
1092 MapBlockMesh::~MapBlockMesh()
1093 {
1094         m_mesh->drop();
1095         m_mesh = NULL;
1096 }
1097
1098 bool MapBlockMesh::animate(bool faraway, float time, int crack, u32 daynight_ratio)
1099 {
1100         if(!m_has_animation)
1101         {
1102                 m_animation_force_timer = 100000;
1103                 return false;
1104         }
1105
1106         m_animation_force_timer = myrand_range(5, 100);
1107
1108         // Cracks
1109         if(crack != m_last_crack)
1110         {
1111                 for(std::map<u32, std::string>::iterator
1112                                 i = m_crack_materials.begin();
1113                                 i != m_crack_materials.end(); i++)
1114                 {
1115                         scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1116                         std::string basename = i->second;
1117
1118                         // Create new texture name from original
1119                         ITextureSource *tsrc = m_gamedef->getTextureSource();
1120                         std::ostringstream os;
1121                         os<<basename<<crack;
1122                         AtlasPointer ap = tsrc->getTexture(os.str());
1123                         buf->getMaterial().setTexture(0, ap.atlas);
1124                 }
1125
1126                 m_last_crack = crack;
1127         }
1128         
1129         // Texture animation
1130         for(std::map<u32, TileSpec>::iterator
1131                         i = m_animation_tiles.begin();
1132                         i != m_animation_tiles.end(); i++)
1133         {
1134                 const TileSpec &tile = i->second;
1135                 // Figure out current frame
1136                 int frameoffset = m_animation_frame_offsets[i->first];
1137                 int frame = (int)(time * 1000 / tile.animation_frame_length_ms
1138                                 + frameoffset) % tile.animation_frame_count;
1139                 // If frame doesn't change, skip
1140                 if(frame == m_animation_frames[i->first])
1141                         continue;
1142
1143                 m_animation_frames[i->first] = frame;
1144
1145                 scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1146                 ITextureSource *tsrc = m_gamedef->getTextureSource();
1147
1148                 // Create new texture name from original
1149                 std::ostringstream os(std::ios::binary);
1150                 os<<tsrc->getTextureName(tile.texture.id);
1151                 os<<"^[verticalframe:"<<(int)tile.animation_frame_count<<":"<<frame;
1152                 // Set the texture
1153                 AtlasPointer ap = tsrc->getTexture(os.str());
1154                 buf->getMaterial().setTexture(0, ap.atlas);
1155         }
1156
1157         // Day-night transition
1158         if(daynight_ratio != m_last_daynight_ratio)
1159         {
1160                 for(std::map<u32, std::map<u32, std::pair<u8, u8> > >::iterator
1161                                 i = m_daynight_diffs.begin();
1162                                 i != m_daynight_diffs.end(); i++)
1163                 {
1164                         scene::IMeshBuffer *buf = m_mesh->getMeshBuffer(i->first);
1165                         video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices();
1166                         for(std::map<u32, std::pair<u8, u8 > >::iterator
1167                                         j = i->second.begin();
1168                                         j != i->second.end(); j++)
1169                         {
1170                                 u32 vertexIndex = j->first;
1171                                 u8 day = j->second.first;
1172                                 u8 night = j->second.second;
1173                                 finalColorBlend(vertices[vertexIndex].Color,
1174                                                 day, night, daynight_ratio);
1175                         }
1176                 }
1177                 m_last_daynight_ratio = daynight_ratio;
1178         }
1179
1180         return true;
1181 }
1182
1183 /*
1184         MeshCollector
1185 */
1186
1187 void MeshCollector::append(const TileSpec &tile,
1188                 const video::S3DVertex *vertices, u32 numVertices,
1189                 const u16 *indices, u32 numIndices)
1190 {
1191         PreMeshBuffer *p = NULL;
1192         for(u32 i=0; i<prebuffers.size(); i++)
1193         {
1194                 PreMeshBuffer &pp = prebuffers[i];
1195                 if(pp.tile != tile)
1196                         continue;
1197
1198                 p = &pp;
1199                 break;
1200         }
1201
1202         if(p == NULL)
1203         {
1204                 PreMeshBuffer pp;
1205                 pp.tile = tile;
1206                 prebuffers.push_back(pp);
1207                 p = &prebuffers[prebuffers.size()-1];
1208         }
1209
1210         u32 vertex_count = p->vertices.size();
1211         for(u32 i=0; i<numIndices; i++)
1212         {
1213                 u32 j = indices[i] + vertex_count;
1214                 if(j > 65535)
1215                 {
1216                         dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
1217                         // NOTE: Fix is to just add an another MeshBuffer
1218                 }
1219                 p->indices.push_back(j);
1220         }
1221         for(u32 i=0; i<numVertices; i++)
1222         {
1223                 p->vertices.push_back(vertices[i]);
1224         }
1225 }