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