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