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