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