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