]> git.lizzy.rs Git - minetest.git/blob - src/content_mapblock.cpp
Environment & IGameDef code refactoring (#4985)
[minetest.git] / src / content_mapblock.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "content_mapblock.h"
21 #include "util/numeric.h"
22 #include "util/directiontables.h"
23 #include "mapblock_mesh.h" // For MapBlock_LightColor() and MeshCollector
24 #include "settings.h"
25 #include "nodedef.h"
26 #include "client/tile.h"
27 #include "mesh.h"
28 #include <IMeshManipulator.h>
29 #include "client.h"
30 #include "log.h"
31 #include "noise.h"
32
33
34 // Create a cuboid.
35 //  collector - the MeshCollector for the resulting polygons
36 //  box       - the position and size of the box
37 //  tiles     - the tiles (materials) to use (for all 6 faces)
38 //  tilecount - number of entries in tiles, 1<=tilecount<=6
39 //  c         - vertex colour - used for all
40 //  txc       - texture coordinates - this is a list of texture coordinates
41 //              for the opposite corners of each face - therefore, there
42 //              should be (2+2)*6=24 values in the list. Alternatively, pass
43 //              NULL to use the entire texture for each face. The order of
44 //              the faces in the list is up-down-right-left-back-front
45 //              (compatible with ContentFeatures). If you specified 0,0,1,1
46 //              for each face, that would be the same as passing NULL.
47 void makeCuboid(MeshCollector *collector, const aabb3f &box,
48         TileSpec *tiles, int tilecount, video::SColor &c, const f32* txc)
49 {
50         assert(tilecount >= 1 && tilecount <= 6); // pre-condition
51
52         v3f min = box.MinEdge;
53         v3f max = box.MaxEdge;
54
55
56
57         if(txc == NULL) {
58                 static const f32 txc_default[24] = {
59                         0,0,1,1,
60                         0,0,1,1,
61                         0,0,1,1,
62                         0,0,1,1,
63                         0,0,1,1,
64                         0,0,1,1
65                 };
66                 txc = txc_default;
67         }
68
69         video::S3DVertex vertices[24] =
70         {
71                 // up
72                 video::S3DVertex(min.X,max.Y,max.Z, 0,1,0, c, txc[0],txc[1]),
73                 video::S3DVertex(max.X,max.Y,max.Z, 0,1,0, c, txc[2],txc[1]),
74                 video::S3DVertex(max.X,max.Y,min.Z, 0,1,0, c, txc[2],txc[3]),
75                 video::S3DVertex(min.X,max.Y,min.Z, 0,1,0, c, txc[0],txc[3]),
76                 // down
77                 video::S3DVertex(min.X,min.Y,min.Z, 0,-1,0, c, txc[4],txc[5]),
78                 video::S3DVertex(max.X,min.Y,min.Z, 0,-1,0, c, txc[6],txc[5]),
79                 video::S3DVertex(max.X,min.Y,max.Z, 0,-1,0, c, txc[6],txc[7]),
80                 video::S3DVertex(min.X,min.Y,max.Z, 0,-1,0, c, txc[4],txc[7]),
81                 // right
82                 video::S3DVertex(max.X,max.Y,min.Z, 1,0,0, c, txc[ 8],txc[9]),
83                 video::S3DVertex(max.X,max.Y,max.Z, 1,0,0, c, txc[10],txc[9]),
84                 video::S3DVertex(max.X,min.Y,max.Z, 1,0,0, c, txc[10],txc[11]),
85                 video::S3DVertex(max.X,min.Y,min.Z, 1,0,0, c, txc[ 8],txc[11]),
86                 // left
87                 video::S3DVertex(min.X,max.Y,max.Z, -1,0,0, c, txc[12],txc[13]),
88                 video::S3DVertex(min.X,max.Y,min.Z, -1,0,0, c, txc[14],txc[13]),
89                 video::S3DVertex(min.X,min.Y,min.Z, -1,0,0, c, txc[14],txc[15]),
90                 video::S3DVertex(min.X,min.Y,max.Z, -1,0,0, c, txc[12],txc[15]),
91                 // back
92                 video::S3DVertex(max.X,max.Y,max.Z, 0,0,1, c, txc[16],txc[17]),
93                 video::S3DVertex(min.X,max.Y,max.Z, 0,0,1, c, txc[18],txc[17]),
94                 video::S3DVertex(min.X,min.Y,max.Z, 0,0,1, c, txc[18],txc[19]),
95                 video::S3DVertex(max.X,min.Y,max.Z, 0,0,1, c, txc[16],txc[19]),
96                 // front
97                 video::S3DVertex(min.X,max.Y,min.Z, 0,0,-1, c, txc[20],txc[21]),
98                 video::S3DVertex(max.X,max.Y,min.Z, 0,0,-1, c, txc[22],txc[21]),
99                 video::S3DVertex(max.X,min.Y,min.Z, 0,0,-1, c, txc[22],txc[23]),
100                 video::S3DVertex(min.X,min.Y,min.Z, 0,0,-1, c, txc[20],txc[23]),
101         };
102
103         for(int i = 0; i < 6; i++)
104                                 {
105                                 switch (tiles[MYMIN(i, tilecount-1)].rotation)
106                                 {
107                                 case 0:
108                                         break;
109                                 case 1: //R90
110                                         for (int x = 0; x < 4; x++)
111                                                 vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
112                                         break;
113                                 case 2: //R180
114                                         for (int x = 0; x < 4; x++)
115                                                 vertices[i*4+x].TCoords.rotateBy(180,irr::core::vector2df(0, 0));
116                                         break;
117                                 case 3: //R270
118                                         for (int x = 0; x < 4; x++)
119                                                 vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
120                                         break;
121                                 case 4: //FXR90
122                                         for (int x = 0; x < 4; x++){
123                                                 vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X;
124                                                 vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
125                                         }
126                                         break;
127                                 case 5: //FXR270
128                                         for (int x = 0; x < 4; x++){
129                                                 vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X;
130                                                 vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
131                                         }
132                                         break;
133                                 case 6: //FYR90
134                                         for (int x = 0; x < 4; x++){
135                                                 vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y;
136                                                 vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
137                                         }
138                                         break;
139                                 case 7: //FYR270
140                                         for (int x = 0; x < 4; x++){
141                                                 vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y;
142                                                 vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
143                                         }
144                                         break;
145                                 case 8: //FX
146                                         for (int x = 0; x < 4; x++){
147                                                 vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X;
148                                         }
149                                         break;
150                                 case 9: //FY
151                                         for (int x = 0; x < 4; x++){
152                                                 vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y;
153                                         }
154                                         break;
155                                 default:
156                                         break;
157                                 }
158                         }
159         u16 indices[] = {0,1,2,2,3,0};
160         // Add to mesh collector
161         for (s32 j = 0; j < 24; j += 4) {
162                 int tileindex = MYMIN(j / 4, tilecount - 1);
163                 collector->append(tiles[tileindex], vertices + j, 4, indices, 6);
164         }
165 }
166
167 static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef,
168                 MeshMakeData *data, MapNode n, int v, int *neighbors)
169 {
170         MapNode n2 = data->m_vmanip.getNodeNoEx(p);
171         if (nodedef->nodeboxConnects(n, n2, v))
172                 *neighbors |= v;
173 }
174
175 // For use in mapblock_mesh_generate_special
176 // X,Y,Z of position must be -1,0,1
177 // This expression is a simplification of
178 // 3 * 3 * (pos.X + 1) + 3 * (pos.Y + 1) + (pos.Z + 1)
179 static inline int NeighborToIndex(const v3s16 &pos)
180 {
181         return 9 * pos.X + 3 * pos.Y + pos.Z + 13;
182 }
183
184 /*
185         TODO: Fix alpha blending for special nodes
186         Currently only the last element rendered is blended correct
187 */
188 void mapblock_mesh_generate_special(MeshMakeData *data,
189                 MeshCollector &collector)
190 {
191         INodeDefManager *nodedef = data->m_client->ndef();
192         scene::ISceneManager* smgr = data->m_client->getSceneManager();
193         scene::IMeshManipulator* meshmanip = smgr->getMeshManipulator();
194
195         // 0ms
196         //TimeTaker timer("mapblock_mesh_generate_special()");
197
198         /*
199                 Some settings
200         */
201         bool enable_mesh_cache  = g_settings->getBool("enable_mesh_cache");
202
203         v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
204
205         for(s16 z = 0; z < MAP_BLOCKSIZE; z++)
206         for(s16 y = 0; y < MAP_BLOCKSIZE; y++)
207         for(s16 x = 0; x < MAP_BLOCKSIZE; x++)
208         {
209                 v3s16 p(x,y,z);
210
211                 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
212                 const ContentFeatures &f = nodedef->get(n);
213
214                 // Only solidness=0 stuff is drawn here
215                 if(f.solidness != 0)
216                         continue;
217
218                 switch(f.drawtype){
219                 default:
220                         infostream << "Got " << f.drawtype << std::endl;
221                         FATAL_ERROR("Unknown drawtype");
222                         break;
223                 case NDT_AIRLIKE:
224                         break;
225                 case NDT_LIQUID:
226                 {
227                         /*
228                                 Add water sources to mesh if using new style
229                         */
230                         TileSpec tile_liquid = f.special_tiles[0];
231                         TileSpec tile_liquid_bfculled = getNodeTile(n, p, v3s16(0,0,0), data);
232
233                         bool top_is_same_liquid = false;
234                         MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
235                         content_t c_flowing = nodedef->getId(f.liquid_alternative_flowing);
236                         content_t c_source = nodedef->getId(f.liquid_alternative_source);
237                         if(ntop.getContent() == c_flowing || ntop.getContent() == c_source)
238                                 top_is_same_liquid = true;
239
240                         u16 l = getInteriorLight(n, 0, nodedef);
241                         video::SColor c = MapBlock_LightColor(f.alpha, l, f.light_source);
242
243                         /*
244                                 Generate sides
245                          */
246                         v3s16 side_dirs[4] = {
247                                 v3s16(1,0,0),
248                                 v3s16(-1,0,0),
249                                 v3s16(0,0,1),
250                                 v3s16(0,0,-1),
251                         };
252                         for(u32 i=0; i<4; i++)
253                         {
254                                 v3s16 dir = side_dirs[i];
255
256                                 MapNode neighbor = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
257                                 content_t neighbor_content = neighbor.getContent();
258                                 const ContentFeatures &n_feat = nodedef->get(neighbor_content);
259                                 MapNode n_top = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir+ v3s16(0,1,0));
260                                 content_t n_top_c = n_top.getContent();
261
262                                 if(neighbor_content == CONTENT_IGNORE)
263                                         continue;
264
265                                 /*
266                                         If our topside is liquid and neighbor's topside
267                                         is liquid, don't draw side face
268                                 */
269                                 if(top_is_same_liquid && (n_top_c == c_flowing ||
270                                                 n_top_c == c_source || n_top_c == CONTENT_IGNORE))
271                                         continue;
272
273                                 // Don't draw face if neighbor is blocking the view
274                                 if(n_feat.solidness == 2)
275                                         continue;
276
277                                 bool neighbor_is_same_liquid = (neighbor_content == c_source
278                                                 || neighbor_content == c_flowing);
279
280                                 // Don't draw any faces if neighbor same is liquid and top is
281                                 // same liquid
282                                 if(neighbor_is_same_liquid && !top_is_same_liquid)
283                                         continue;
284
285                                 // Use backface culled material if neighbor doesn't have a
286                                 // solidness of 0
287                                 const TileSpec *current_tile = &tile_liquid;
288                                 if(n_feat.solidness != 0 || n_feat.visual_solidness != 0)
289                                         current_tile = &tile_liquid_bfculled;
290
291                                 video::S3DVertex vertices[4] =
292                                 {
293                                         video::S3DVertex(-BS/2,0,BS/2,0,0,0, c, 0,1),
294                                         video::S3DVertex(BS/2,0,BS/2,0,0,0, c, 1,1),
295                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
296                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
297                                 };
298
299                                 /*
300                                         If our topside is liquid, set upper border of face
301                                         at upper border of node
302                                 */
303                                 if (top_is_same_liquid) {
304                                         vertices[2].Pos.Y = 0.5 * BS;
305                                         vertices[3].Pos.Y = 0.5 * BS;
306                                 } else {
307                                 /*
308                                         Otherwise upper position of face is liquid level
309                                 */
310                                         vertices[2].Pos.Y = 0.5 * BS;
311                                         vertices[3].Pos.Y = 0.5 * BS;
312                                 }
313                                 /*
314                                         If neighbor is liquid, lower border of face is liquid level
315                                 */
316                                 if (neighbor_is_same_liquid) {
317                                         vertices[0].Pos.Y = 0.5 * BS;
318                                         vertices[1].Pos.Y = 0.5 * BS;
319                                 } else {
320                                 /*
321                                         If neighbor is not liquid, lower border of face is
322                                         lower border of node
323                                 */
324                                         vertices[0].Pos.Y = -0.5 * BS;
325                                         vertices[1].Pos.Y = -0.5 * BS;
326                                 }
327
328                                 for(s32 j=0; j<4; j++)
329                                 {
330                                         if(dir == v3s16(0,0,1))
331                                                 vertices[j].Pos.rotateXZBy(0);
332                                         if(dir == v3s16(0,0,-1))
333                                                 vertices[j].Pos.rotateXZBy(180);
334                                         if(dir == v3s16(-1,0,0))
335                                                 vertices[j].Pos.rotateXZBy(90);
336                                         if(dir == v3s16(1,0,-0))
337                                                 vertices[j].Pos.rotateXZBy(-90);
338
339                                         // Do this to not cause glitches when two liquids are
340                                         // side-by-side
341                                         /*if(neighbor_is_same_liquid == false){
342                                                 vertices[j].Pos.X *= 0.98;
343                                                 vertices[j].Pos.Z *= 0.98;
344                                         }*/
345
346                                         vertices[j].Pos += intToFloat(p, BS);
347                                 }
348
349                                 u16 indices[] = {0,1,2,2,3,0};
350                                 // Add to mesh collector
351                                 collector.append(*current_tile, vertices, 4, indices, 6);
352                         }
353
354                         /*
355                                 Generate top
356                          */
357                         if(top_is_same_liquid)
358                                 continue;
359
360                         video::S3DVertex vertices[4] =
361                         {
362                                 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
363                                 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
364                                 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,0),
365                                 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,0),
366                         };
367
368                         v3f offset(p.X * BS, (p.Y + 0.5) * BS, p.Z * BS);
369                         for(s32 i=0; i<4; i++)
370                         {
371                                 vertices[i].Pos += offset;
372                         }
373
374                         u16 indices[] = {0,1,2,2,3,0};
375                         // Add to mesh collector
376                         collector.append(tile_liquid, vertices, 4, indices, 6);
377                 break;}
378                 case NDT_FLOWINGLIQUID:
379                 {
380                         /*
381                                 Add flowing liquid to mesh
382                         */
383                         TileSpec tile_liquid = f.special_tiles[0];
384                         TileSpec tile_liquid_bfculled = f.special_tiles[1];
385
386                         bool top_is_same_liquid = false;
387                         MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
388                         content_t c_flowing = nodedef->getId(f.liquid_alternative_flowing);
389                         content_t c_source = nodedef->getId(f.liquid_alternative_source);
390                         if(ntop.getContent() == c_flowing || ntop.getContent() == c_source)
391                                 top_is_same_liquid = true;
392
393                         u16 l = 0;
394                         // If this liquid emits light and doesn't contain light, draw
395                         // it at what it emits, for an increased effect
396                         u8 light_source = nodedef->get(n).light_source;
397                         if(light_source != 0){
398                                 l = decode_light(light_source);
399                                 l = l | (l<<8);
400                         }
401                         // Use the light of the node on top if possible
402                         else if(nodedef->get(ntop).param_type == CPT_LIGHT)
403                                 l = getInteriorLight(ntop, 0, nodedef);
404                         // Otherwise use the light of this node (the liquid)
405                         else
406                                 l = getInteriorLight(n, 0, nodedef);
407                         video::SColor c = MapBlock_LightColor(f.alpha, l, f.light_source);
408
409                         u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8);
410
411                         // Neighbor liquid levels (key = relative position)
412                         // Includes current node
413
414                         struct NeighborData {
415                                 f32 level;
416                                 content_t content;
417                                 u8 flags;
418                         };
419                         NeighborData neighbor_data_matrix[27];
420
421                         const u8 neighborflag_top_is_same_liquid = 0x01;
422                         v3s16 neighbor_dirs[9] = {
423                                 v3s16(0,0,0),
424                                 v3s16(0,0,1),
425                                 v3s16(0,0,-1),
426                                 v3s16(1,0,0),
427                                 v3s16(-1,0,0),
428                                 v3s16(1,0,1),
429                                 v3s16(-1,0,-1),
430                                 v3s16(1,0,-1),
431                                 v3s16(-1,0,1),
432                         };
433                         for(u32 i=0; i<9; i++)
434                         {
435                                 content_t content = CONTENT_AIR;
436                                 float level = -0.5 * BS;
437                                 u8 flags = 0;
438                                 // Check neighbor
439                                 v3s16 p2 = p + neighbor_dirs[i];
440                                 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
441                                 if(n2.getContent() != CONTENT_IGNORE)
442                                 {
443                                         content = n2.getContent();
444
445                                         if(n2.getContent() == c_source)
446                                                 level = 0.5 * BS;
447                                         else if(n2.getContent() == c_flowing){
448                                                 u8 liquid_level = (n2.param2&LIQUID_LEVEL_MASK);
449                                                 if (liquid_level <= LIQUID_LEVEL_MAX+1-range)
450                                                         liquid_level = 0;
451                                                 else
452                                                         liquid_level -= (LIQUID_LEVEL_MAX+1-range);
453                                                 level = (-0.5 + ((float)liquid_level + 0.5) / (float)range) * BS;
454                                         }
455
456                                         // Check node above neighbor.
457                                         // NOTE: This doesn't get executed if neighbor
458                                         //       doesn't exist
459                                         p2.Y += 1;
460                                         n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
461                                         if(n2.getContent() == c_source ||
462                                                         n2.getContent() == c_flowing)
463                                                 flags |= neighborflag_top_is_same_liquid;
464                                 }
465
466                                 NeighborData &neighbor_data =
467                                         neighbor_data_matrix[NeighborToIndex(neighbor_dirs[i])];
468
469                                 neighbor_data.level = level;
470                                 neighbor_data.content = content;
471                                 neighbor_data.flags = flags;
472                         }
473
474                         // Corner heights (average between four liquids)
475                         f32 corner_levels[4];
476
477                         v3s16 halfdirs[4] = {
478                                 v3s16(0,0,0),
479                                 v3s16(1,0,0),
480                                 v3s16(1,0,1),
481                                 v3s16(0,0,1),
482                         };
483                         for(u32 i=0; i<4; i++)
484                         {
485                                 v3s16 cornerdir = halfdirs[i];
486                                 float cornerlevel = 0;
487                                 u32 valid_count = 0;
488                                 u32 air_count = 0;
489                                 for(u32 j=0; j<4; j++)
490                                 {
491                                         v3s16 neighbordir = cornerdir - halfdirs[j];
492
493                                         NeighborData &neighbor_data =
494                                                 neighbor_data_matrix[NeighborToIndex(neighbordir)];
495                                         content_t content = neighbor_data.content;
496                                         // If top is liquid, draw starting from top of node
497                                         if (neighbor_data.flags & neighborflag_top_is_same_liquid)
498                                         {
499                                                 cornerlevel = 0.5*BS;
500                                                 valid_count = 1;
501                                                 break;
502                                         }
503                                         // Source is always the same height
504                                         else if(content == c_source)
505                                         {
506                                                 cornerlevel = 0.5 * BS;
507                                                 valid_count = 1;
508                                                 break;
509                                         }
510                                         // Flowing liquid has level information
511                                         else if(content == c_flowing)
512                                         {
513                                                 cornerlevel += neighbor_data.level;
514                                                 valid_count++;
515                                         }
516                                         else if(content == CONTENT_AIR)
517                                         {
518                                                 air_count++;
519                                         }
520                                 }
521                                 if(air_count >= 2)
522                                         cornerlevel = -0.5*BS+0.2;
523                                 else if(valid_count > 0)
524                                         cornerlevel /= valid_count;
525                                 corner_levels[i] = cornerlevel;
526                         }
527
528                         /*
529                                 Generate sides
530                         */
531
532                         v3s16 side_dirs[4] = {
533                                 v3s16(1,0,0),
534                                 v3s16(-1,0,0),
535                                 v3s16(0,0,1),
536                                 v3s16(0,0,-1),
537                         };
538                         s16 side_corners[4][2] = {
539                                 {1, 2},
540                                 {3, 0},
541                                 {2, 3},
542                                 {0, 1},
543                         };
544                         for(u32 i=0; i<4; i++)
545                         {
546                                 v3s16 dir = side_dirs[i];
547
548                                 NeighborData& neighbor_data =
549                                         neighbor_data_matrix[NeighborToIndex(dir)];
550                                 /*
551                                         If our topside is liquid and neighbor's topside
552                                         is liquid, don't draw side face
553                                 */
554                                 if (top_is_same_liquid &&
555                                                 neighbor_data.flags & neighborflag_top_is_same_liquid)
556                                         continue;
557
558                                 content_t neighbor_content = neighbor_data.content;
559                                 const ContentFeatures &n_feat = nodedef->get(neighbor_content);
560
561                                 // Don't draw face if neighbor is blocking the view
562                                 if(n_feat.solidness == 2)
563                                         continue;
564
565                                 bool neighbor_is_same_liquid = (neighbor_content == c_source
566                                                 || neighbor_content == c_flowing);
567
568                                 // Don't draw any faces if neighbor same is liquid and top is
569                                 // same liquid
570                                 if(neighbor_is_same_liquid == true
571                                                 && top_is_same_liquid == false)
572                                         continue;
573
574                                 // Use backface culled material if neighbor doesn't have a
575                                 // solidness of 0
576                                 const TileSpec *current_tile = &tile_liquid;
577                                 if(n_feat.solidness != 0 || n_feat.visual_solidness != 0)
578                                         current_tile = &tile_liquid_bfculled;
579
580                                 video::S3DVertex vertices[4] =
581                                 {
582                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
583                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
584                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
585                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
586                                 };
587
588                                 /*
589                                         If our topside is liquid, set upper border of face
590                                         at upper border of node
591                                 */
592                                 if(top_is_same_liquid)
593                                 {
594                                         vertices[2].Pos.Y = 0.5*BS;
595                                         vertices[3].Pos.Y = 0.5*BS;
596                                 }
597                                 /*
598                                         Otherwise upper position of face is corner levels
599                                 */
600                                 else
601                                 {
602                                         vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
603                                         vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
604                                 }
605
606                                 /*
607                                         If neighbor is liquid, lower border of face is corner
608                                         liquid levels
609                                 */
610                                 if(neighbor_is_same_liquid)
611                                 {
612                                         vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
613                                         vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
614                                 }
615                                 /*
616                                         If neighbor is not liquid, lower border of face is
617                                         lower border of node
618                                 */
619                                 else
620                                 {
621                                         vertices[0].Pos.Y = -0.5*BS;
622                                         vertices[1].Pos.Y = -0.5*BS;
623                                 }
624
625                                 for(s32 j=0; j<4; j++)
626                                 {
627                                         if(dir == v3s16(0,0,1))
628                                                 vertices[j].Pos.rotateXZBy(0);
629                                         if(dir == v3s16(0,0,-1))
630                                                 vertices[j].Pos.rotateXZBy(180);
631                                         if(dir == v3s16(-1,0,0))
632                                                 vertices[j].Pos.rotateXZBy(90);
633                                         if(dir == v3s16(1,0,-0))
634                                                 vertices[j].Pos.rotateXZBy(-90);
635
636                                         // Do this to not cause glitches when two liquids are
637                                         // side-by-side
638                                         /*if(neighbor_is_same_liquid == false){
639                                                 vertices[j].Pos.X *= 0.98;
640                                                 vertices[j].Pos.Z *= 0.98;
641                                         }*/
642
643                                         vertices[j].Pos += intToFloat(p, BS);
644                                 }
645
646                                 u16 indices[] = {0,1,2,2,3,0};
647                                 // Add to mesh collector
648                                 collector.append(*current_tile, vertices, 4, indices, 6);
649                         }
650
651                         /*
652                                 Generate top side, if appropriate
653                         */
654
655                         if(top_is_same_liquid == false)
656                         {
657                                 video::S3DVertex vertices[4] =
658                                 {
659                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
660                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
661                                         video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,0),
662                                         video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,0),
663                                 };
664
665                                 // To get backface culling right, the vertices need to go
666                                 // clockwise around the front of the face. And we happened to
667                                 // calculate corner levels in exact reverse order.
668                                 s32 corner_resolve[4] = {3,2,1,0};
669
670                                 for(s32 i=0; i<4; i++)
671                                 {
672                                         //vertices[i].Pos.Y += liquid_level;
673                                         //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
674                                         s32 j = corner_resolve[i];
675                                         vertices[i].Pos.Y += corner_levels[j];
676                                         vertices[i].Pos += intToFloat(p, BS);
677                                 }
678
679                                 // Default downwards-flowing texture animation goes from
680                                 // -Z towards +Z, thus the direction is +Z.
681                                 // Rotate texture to make animation go in flow direction
682                                 // Positive if liquid moves towards +Z
683                                 f32 dz = (corner_levels[side_corners[3][0]] +
684                                                 corner_levels[side_corners[3][1]]) -
685                                                 (corner_levels[side_corners[2][0]] +
686                                                 corner_levels[side_corners[2][1]]);
687                                 // Positive if liquid moves towards +X
688                                 f32 dx = (corner_levels[side_corners[1][0]] +
689                                                 corner_levels[side_corners[1][1]]) -
690                                                 (corner_levels[side_corners[0][0]] +
691                                                 corner_levels[side_corners[0][1]]);
692                                 f32 tcoord_angle = atan2(dz, dx) * core::RADTODEG ;
693                                 v2f tcoord_center(0.5, 0.5);
694                                 v2f tcoord_translate(
695                                                 blockpos_nodes.Z + z,
696                                                 blockpos_nodes.X + x);
697                                 tcoord_translate.rotateBy(tcoord_angle);
698                                 tcoord_translate.X -= floor(tcoord_translate.X);
699                                 tcoord_translate.Y -= floor(tcoord_translate.Y);
700
701                                 for(s32 i=0; i<4; i++)
702                                 {
703                                         vertices[i].TCoords.rotateBy(
704                                                         tcoord_angle,
705                                                         tcoord_center);
706                                         vertices[i].TCoords += tcoord_translate;
707                                 }
708
709                                 v2f t = vertices[0].TCoords;
710                                 vertices[0].TCoords = vertices[2].TCoords;
711                                 vertices[2].TCoords = t;
712
713                                 u16 indices[] = {0,1,2,2,3,0};
714                                 // Add to mesh collector
715                                 collector.append(tile_liquid, vertices, 4, indices, 6);
716                         }
717                 break;}
718                 case NDT_GLASSLIKE:
719                 {
720                         TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data);
721
722                         u16 l = getInteriorLight(n, 1, nodedef);
723                         video::SColor c = MapBlock_LightColor(255, l, f.light_source);
724
725                         for(u32 j=0; j<6; j++)
726                         {
727                                 // Check this neighbor
728                                 v3s16 dir = g_6dirs[j];
729                                 v3s16 n2p = blockpos_nodes + p + dir;
730                                 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
731                                 // Don't make face if neighbor is of same type
732                                 if(n2.getContent() == n.getContent())
733                                         continue;
734
735                                 // The face at Z+
736                                 video::S3DVertex vertices[4] = {
737                                         video::S3DVertex(-BS/2,-BS/2,BS/2, dir.X,dir.Y,dir.Z, c, 1,1),
738                                         video::S3DVertex(BS/2,-BS/2,BS/2, dir.X,dir.Y,dir.Z, c, 0,1),
739                                         video::S3DVertex(BS/2,BS/2,BS/2, dir.X,dir.Y,dir.Z, c, 0,0),
740                                         video::S3DVertex(-BS/2,BS/2,BS/2, dir.X,dir.Y,dir.Z, c, 1,0),
741                                 };
742
743                                 // Rotations in the g_6dirs format
744                                 if(j == 0) // Z+
745                                         for(u16 i=0; i<4; i++)
746                                                 vertices[i].Pos.rotateXZBy(0);
747                                 else if(j == 1) // Y+
748                                         for(u16 i=0; i<4; i++)
749                                                 vertices[i].Pos.rotateYZBy(-90);
750                                 else if(j == 2) // X+
751                                         for(u16 i=0; i<4; i++)
752                                                 vertices[i].Pos.rotateXZBy(-90);
753                                 else if(j == 3) // Z-
754                                         for(u16 i=0; i<4; i++)
755                                                 vertices[i].Pos.rotateXZBy(180);
756                                 else if(j == 4) // Y-
757                                         for(u16 i=0; i<4; i++)
758                                                 vertices[i].Pos.rotateYZBy(90);
759                                 else if(j == 5) // X-
760                                         for(u16 i=0; i<4; i++)
761                                                 vertices[i].Pos.rotateXZBy(90);
762
763                                 for(u16 i=0; i<4; i++){
764                                         vertices[i].Pos += intToFloat(p, BS);
765                                 }
766
767                                 u16 indices[] = {0,1,2,2,3,0};
768                                 // Add to mesh collector
769                                 collector.append(tile, vertices, 4, indices, 6);
770                         }
771                 break;}
772                 case NDT_GLASSLIKE_FRAMED_OPTIONAL:
773                         // This is always pre-converted to something else
774                         FATAL_ERROR("NDT_GLASSLIKE_FRAMED_OPTIONAL not pre-converted as expected");
775                         break;
776                 case NDT_GLASSLIKE_FRAMED:
777                 {
778                         static const v3s16 dirs[6] = {
779                                 v3s16( 0, 1, 0),
780                                 v3s16( 0,-1, 0),
781                                 v3s16( 1, 0, 0),
782                                 v3s16(-1, 0, 0),
783                                 v3s16( 0, 0, 1),
784                                 v3s16( 0, 0,-1)
785                         };
786
787                         u8 i;
788                         TileSpec tiles[6];
789                         for (i = 0; i < 6; i++)
790                                 tiles[i] = getNodeTile(n, p, dirs[i], data);
791
792                         TileSpec glass_tiles[6];
793                         if (tiles[1].texture && tiles[2].texture && tiles[3].texture) {
794                                 glass_tiles[0] = tiles[2];
795                                 glass_tiles[1] = tiles[3];
796                                 glass_tiles[2] = tiles[1];
797                                 glass_tiles[3] = tiles[1];
798                                 glass_tiles[4] = tiles[1];
799                                 glass_tiles[5] = tiles[1];
800                         } else {
801                                 for (i = 0; i < 6; i++)
802                                         glass_tiles[i] = tiles[1];
803                         }
804
805                         u8 param2 = n.getParam2();
806                         bool H_merge = ! bool(param2 & 128);
807                         bool V_merge = ! bool(param2 & 64);
808                         param2  = param2 & 63;
809
810                         u16 l = getInteriorLight(n, 1, nodedef);
811                         video::SColor c = MapBlock_LightColor(255, l, f.light_source);
812                         v3f pos = intToFloat(p, BS);
813                         static const float a = BS / 2;
814                         static const float g = a - 0.003;
815                         static const float b = .876 * ( BS / 2 );
816
817                         static const aabb3f frame_edges[12] = {
818                                 aabb3f( b, b,-a, a, a, a), // y+
819                                 aabb3f(-a, b,-a,-b, a, a), // y+
820                                 aabb3f( b,-a,-a, a,-b, a), // y-
821                                 aabb3f(-a,-a,-a,-b,-b, a), // y-
822                                 aabb3f( b,-a, b, a, a, a), // x+
823                                 aabb3f( b,-a,-a, a, a,-b), // x+
824                                 aabb3f(-a,-a, b,-b, a, a), // x-
825                                 aabb3f(-a,-a,-a,-b, a,-b), // x-
826                                 aabb3f(-a, b, b, a, a, a), // z+
827                                 aabb3f(-a,-a, b, a,-b, a), // z+
828                                 aabb3f(-a,-a,-a, a,-b,-b), // z-
829                                 aabb3f(-a, b,-a, a, a,-b)  // z-
830                         };
831                         static const aabb3f glass_faces[6] = {
832                                 aabb3f(-g, g,-g, g, g, g), // y+
833                                 aabb3f(-g,-g,-g, g,-g, g), // y-
834                                 aabb3f( g,-g,-g, g, g, g), // x+
835                                 aabb3f(-g,-g,-g,-g, g, g), // x-
836                                 aabb3f(-g,-g, g, g, g, g), // z+
837                                 aabb3f(-g,-g,-g, g, g,-g)  // z-
838                         };
839
840                         // table of node visible faces, 0 = invisible
841                         int visible_faces[6] = {0,0,0,0,0,0};
842
843                         // table of neighbours, 1 = same type, checked with g_26dirs
844                         int nb[18] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
845
846                         // g_26dirs to check when only horizontal merge is allowed
847                         int nb_H_dirs[8] = {0,2,3,5,10,11,12,13};
848
849                         content_t current = n.getContent();
850                         content_t n2c;
851                         MapNode n2;
852                         v3s16 n2p;
853
854                         // neighbours checks for frames visibility
855
856                         if (!H_merge && V_merge) {
857                                 n2p = blockpos_nodes + p + g_26dirs[1];
858                                 n2 = data->m_vmanip.getNodeNoEx(n2p);
859                                 n2c = n2.getContent();
860                                 if (n2c == current || n2c == CONTENT_IGNORE)
861                                         nb[1] = 1;
862                                 n2p = blockpos_nodes + p + g_26dirs[4];
863                                 n2 = data->m_vmanip.getNodeNoEx(n2p);
864                                 n2c = n2.getContent();
865                                 if (n2c == current || n2c == CONTENT_IGNORE)
866                                         nb[4] = 1;
867                         } else if (H_merge && !V_merge) {
868                                 for(i = 0; i < 8; i++) {
869                                         n2p = blockpos_nodes + p + g_26dirs[nb_H_dirs[i]];
870                                         n2 = data->m_vmanip.getNodeNoEx(n2p);
871                                         n2c = n2.getContent();
872                                         if (n2c == current || n2c == CONTENT_IGNORE)
873                                                 nb[nb_H_dirs[i]] = 1;
874                                 }
875                         } else if (H_merge && V_merge) {
876                                 for(i = 0; i < 18; i++) {
877                                         n2p = blockpos_nodes + p + g_26dirs[i];
878                                         n2 = data->m_vmanip.getNodeNoEx(n2p);
879                                         n2c = n2.getContent();
880                                         if (n2c == current || n2c == CONTENT_IGNORE)
881                                                 nb[i] = 1;
882                                 }
883                         }
884
885                         // faces visibility checks
886
887                         if (!V_merge) {
888                                 visible_faces[0] = 1;
889                                 visible_faces[1] = 1;
890                         } else {
891                                 for(i = 0; i < 2; i++) {
892                                         n2p = blockpos_nodes + p + dirs[i];
893                                         n2 = data->m_vmanip.getNodeNoEx(n2p);
894                                         n2c = n2.getContent();
895                                         if (n2c != current)
896                                                 visible_faces[i] = 1;
897                                 }
898                         }
899
900                         if (!H_merge) {
901                                 visible_faces[2] = 1;
902                                 visible_faces[3] = 1;
903                                 visible_faces[4] = 1;
904                                 visible_faces[5] = 1;
905                         } else {
906                                 for(i = 2; i < 6; i++) {
907                                         n2p = blockpos_nodes + p + dirs[i];
908                                         n2 = data->m_vmanip.getNodeNoEx(n2p);
909                                         n2c = n2.getContent();
910                                         if (n2c != current)
911                                                 visible_faces[i] = 1;
912                                 }
913                         }
914
915                         static const u8 nb_triplet[12*3] = {
916                                 1,2, 7,  1,5, 6,  4,2,15,  4,5,14,
917                                 2,0,11,  2,3,13,  5,0,10,  5,3,12,
918                                 0,1, 8,  0,4,16,  3,4,17,  3,1, 9
919                         };
920
921                         f32 tx1, ty1, tz1, tx2, ty2, tz2;
922                         aabb3f box;
923
924                         for(i = 0; i < 12; i++)
925                         {
926                                 int edge_invisible;
927                                 if (nb[nb_triplet[i*3+2]])
928                                         edge_invisible = nb[nb_triplet[i*3]] & nb[nb_triplet[i*3+1]];
929                                 else
930                                         edge_invisible = nb[nb_triplet[i*3]] ^ nb[nb_triplet[i*3+1]];
931                                 if (edge_invisible)
932                                         continue;
933                                 box = frame_edges[i];
934                                 box.MinEdge += pos;
935                                 box.MaxEdge += pos;
936                                 tx1 = (box.MinEdge.X / BS) + 0.5;
937                                 ty1 = (box.MinEdge.Y / BS) + 0.5;
938                                 tz1 = (box.MinEdge.Z / BS) + 0.5;
939                                 tx2 = (box.MaxEdge.X / BS) + 0.5;
940                                 ty2 = (box.MaxEdge.Y / BS) + 0.5;
941                                 tz2 = (box.MaxEdge.Z / BS) + 0.5;
942                                 f32 txc1[24] = {
943                                         tx1,   1-tz2,   tx2, 1-tz1,
944                                         tx1,     tz1,   tx2,   tz2,
945                                         tz1,   1-ty2,   tz2, 1-ty1,
946                                         1-tz2, 1-ty2, 1-tz1, 1-ty1,
947                                         1-tx2, 1-ty2, 1-tx1, 1-ty1,
948                                         tx1,   1-ty2,   tx2, 1-ty1,
949                                 };
950                                 makeCuboid(&collector, box, &tiles[0], 1, c, txc1);
951                         }
952
953                         for(i = 0; i < 6; i++)
954                         {
955                                 if (!visible_faces[i])
956                                         continue;
957                                 box = glass_faces[i];
958                                 box.MinEdge += pos;
959                                 box.MaxEdge += pos;
960                                 tx1 = (box.MinEdge.X / BS) + 0.5;
961                                 ty1 = (box.MinEdge.Y / BS) + 0.5;
962                                 tz1 = (box.MinEdge.Z / BS) + 0.5;
963                                 tx2 = (box.MaxEdge.X / BS) + 0.5;
964                                 ty2 = (box.MaxEdge.Y / BS) + 0.5;
965                                 tz2 = (box.MaxEdge.Z / BS) + 0.5;
966                                 f32 txc2[24] = {
967                                         tx1,   1-tz2,   tx2, 1-tz1,
968                                         tx1,     tz1,   tx2,   tz2,
969                                         tz1,   1-ty2,   tz2, 1-ty1,
970                                         1-tz2, 1-ty2, 1-tz1, 1-ty1,
971                                         1-tx2, 1-ty2, 1-tx1, 1-ty1,
972                                         tx1,   1-ty2,   tx2, 1-ty1,
973                                 };
974                                 makeCuboid(&collector, box, &glass_tiles[i], 1, c, txc2);
975                         }
976
977                         if (param2 > 0 && f.special_tiles[0].texture) {
978                                 // Interior volume level is in range 0 .. 63,
979                                 // convert it to -0.5 .. 0.5
980                                 float vlev = (((float)param2 / 63.0 ) * 2.0 - 1.0);
981                                 TileSpec interior_tiles[6];
982                                 for (i = 0; i < 6; i++)
983                                         interior_tiles[i] = f.special_tiles[0];
984                                 float offset = 0.003;
985                                 box = aabb3f(visible_faces[3] ? -b : -a + offset,
986                                                          visible_faces[1] ? -b : -a + offset,
987                                                          visible_faces[5] ? -b : -a + offset,
988                                                          visible_faces[2] ? b : a - offset,
989                                                          visible_faces[0] ? b * vlev : a * vlev - offset,
990                                                          visible_faces[4] ? b : a - offset);
991                                 box.MinEdge += pos;
992                                 box.MaxEdge += pos;
993                                 tx1 = (box.MinEdge.X / BS) + 0.5;
994                                 ty1 = (box.MinEdge.Y / BS) + 0.5;
995                                 tz1 = (box.MinEdge.Z / BS) + 0.5;
996                                 tx2 = (box.MaxEdge.X / BS) + 0.5;
997                                 ty2 = (box.MaxEdge.Y / BS) + 0.5;
998                                 tz2 = (box.MaxEdge.Z / BS) + 0.5;
999                                 f32 txc3[24] = {
1000                                         tx1,   1-tz2,   tx2, 1-tz1,
1001                                         tx1,     tz1,   tx2,   tz2,
1002                                         tz1,   1-ty2,   tz2, 1-ty1,
1003                                         1-tz2, 1-ty2, 1-tz1, 1-ty1,
1004                                         1-tx2, 1-ty2, 1-tx1, 1-ty1,
1005                                         tx1,   1-ty2,   tx2, 1-ty1,
1006                                 };
1007                                 makeCuboid(&collector, box, interior_tiles, 6, c,  txc3);
1008                         }
1009                 break;}
1010                 case NDT_ALLFACES:
1011                 {
1012                         TileSpec tile_leaves = getNodeTile(n, p,
1013                                         v3s16(0,0,0), data);
1014
1015                         u16 l = getInteriorLight(n, 1, nodedef);
1016                         video::SColor c = MapBlock_LightColor(255, l, f.light_source);
1017
1018                         v3f pos = intToFloat(p, BS);
1019                         aabb3f box(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2);
1020                         box.MinEdge += pos;
1021                         box.MaxEdge += pos;
1022                         makeCuboid(&collector, box, &tile_leaves, 1, c, NULL);
1023                 break;}
1024                 case NDT_ALLFACES_OPTIONAL:
1025                         // This is always pre-converted to something else
1026                         FATAL_ERROR("NDT_ALLFACES_OPTIONAL not pre-converted");
1027                         break;
1028                 case NDT_TORCHLIKE:
1029                 {
1030                         v3s16 dir = n.getWallMountedDir(nodedef);
1031
1032                         u8 tileindex = 0;
1033                         if(dir == v3s16(0,-1,0)){
1034                                 tileindex = 0; // floor
1035                         } else if(dir == v3s16(0,1,0)){
1036                                 tileindex = 1; // ceiling
1037                         // For backwards compatibility
1038                         } else if(dir == v3s16(0,0,0)){
1039                                 tileindex = 0; // floor
1040                         } else {
1041                                 tileindex = 2; // side
1042                         }
1043
1044                         TileSpec tile = getNodeTileN(n, p, tileindex, data);
1045                         tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
1046                         tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1047
1048                         u16 l = getInteriorLight(n, 1, nodedef);
1049                         video::SColor c = MapBlock_LightColor(255, l, f.light_source);
1050
1051                         float s = BS/2*f.visual_scale;
1052                         // Wall at X+ of node
1053                         video::S3DVertex vertices[4] =
1054                         {
1055                                 video::S3DVertex(-s,-s,0, 0,0,0, c, 0,1),
1056                                 video::S3DVertex( s,-s,0, 0,0,0, c, 1,1),
1057                                 video::S3DVertex( s, s,0, 0,0,0, c, 1,0),
1058                                 video::S3DVertex(-s, s,0, 0,0,0, c, 0,0),
1059                         };
1060
1061                         for(s32 i=0; i<4; i++)
1062                         {
1063                                 if(dir == v3s16(1,0,0))
1064                                         vertices[i].Pos.rotateXZBy(0);
1065                                 if(dir == v3s16(-1,0,0))
1066                                         vertices[i].Pos.rotateXZBy(180);
1067                                 if(dir == v3s16(0,0,1))
1068                                         vertices[i].Pos.rotateXZBy(90);
1069                                 if(dir == v3s16(0,0,-1))
1070                                         vertices[i].Pos.rotateXZBy(-90);
1071                                 if(dir == v3s16(0,-1,0))
1072                                         vertices[i].Pos.rotateXZBy(45);
1073                                 if(dir == v3s16(0,1,0))
1074                                         vertices[i].Pos.rotateXZBy(-45);
1075
1076                                 vertices[i].Pos += intToFloat(p, BS);
1077                         }
1078
1079                         u16 indices[] = {0,1,2,2,3,0};
1080                         // Add to mesh collector
1081                         collector.append(tile, vertices, 4, indices, 6);
1082                 break;}
1083                 case NDT_SIGNLIKE:
1084                 {
1085                         TileSpec tile = getNodeTileN(n, p, 0, data);
1086                         tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
1087                         tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1088
1089                         u16 l = getInteriorLight(n, 0, nodedef);
1090                         video::SColor c = MapBlock_LightColor(255, l, f.light_source);
1091
1092                         float d = (float)BS/16;
1093                         float s = BS/2*f.visual_scale;
1094                         // Wall at X+ of node
1095                         video::S3DVertex vertices[4] =
1096                         {
1097                                 video::S3DVertex(BS/2-d,  s,  s, 0,0,0, c, 0,0),
1098                                 video::S3DVertex(BS/2-d,  s, -s, 0,0,0, c, 1,0),
1099                                 video::S3DVertex(BS/2-d, -s, -s, 0,0,0, c, 1,1),
1100                                 video::S3DVertex(BS/2-d, -s,  s, 0,0,0, c, 0,1),
1101                         };
1102
1103                         v3s16 dir = n.getWallMountedDir(nodedef);
1104
1105                         for(s32 i=0; i<4; i++)
1106                         {
1107                                 if(dir == v3s16(1,0,0))
1108                                         vertices[i].Pos.rotateXZBy(0);
1109                                 if(dir == v3s16(-1,0,0))
1110                                         vertices[i].Pos.rotateXZBy(180);
1111                                 if(dir == v3s16(0,0,1))
1112                                         vertices[i].Pos.rotateXZBy(90);
1113                                 if(dir == v3s16(0,0,-1))
1114                                         vertices[i].Pos.rotateXZBy(-90);
1115                                 if(dir == v3s16(0,-1,0))
1116                                         vertices[i].Pos.rotateXYBy(-90);
1117                                 if(dir == v3s16(0,1,0))
1118                                         vertices[i].Pos.rotateXYBy(90);
1119
1120                                 vertices[i].Pos += intToFloat(p, BS);
1121                         }
1122
1123                         u16 indices[] = {0,1,2,2,3,0};
1124                         // Add to mesh collector
1125                         collector.append(tile, vertices, 4, indices, 6);
1126                 break;}
1127                 case NDT_PLANTLIKE:
1128                 {
1129                         PseudoRandom rng(x<<8 | z | y<<16);
1130
1131                         TileSpec tile = getNodeTileN(n, p, 0, data);
1132                         tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1133
1134                         u16 l = getInteriorLight(n, 1, nodedef);
1135                         video::SColor c = MapBlock_LightColor(255, l, f.light_source);
1136
1137                         float s = BS / 2 * f.visual_scale;
1138                         // add sqrt(2) visual scale
1139                         if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x10) != 0))
1140                                 s *= 1.41421;
1141
1142                         float random_offset_X = .0;
1143                         float random_offset_Z = .0;
1144                         if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x8) != 0)) {
1145                                 random_offset_X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
1146                                 random_offset_Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
1147                         }
1148
1149                         for (int j = 0; j < 4; j++) {
1150                                 video::S3DVertex vertices[4] =
1151                                 {
1152                                         video::S3DVertex(-s,-BS/2, 0, 0,0,0, c, 0,1),
1153                                         video::S3DVertex( s,-BS/2, 0, 0,0,0, c, 1,1),
1154                                         video::S3DVertex( s,-BS/2 + s*2,0, 0,0,0, c, 1,0),
1155                                         video::S3DVertex(-s,-BS/2 + s*2,0, 0,0,0, c, 0,0),
1156                                 };
1157
1158                                 float rotate_degree = 0;
1159                                 u8 p2mesh = 0;
1160                                 if (f.param_type_2 == CPT2_DEGROTATE)
1161                                         rotate_degree = n.param2 * 2;
1162                                 if (f.param_type_2 != CPT2_MESHOPTIONS) {
1163                                         if (j == 0) {
1164                                                 for (u16 i = 0; i < 4; i++)
1165                                                         vertices[i].Pos.rotateXZBy(46 + rotate_degree);
1166                                         } else if (j == 1) {
1167                                                 for (u16 i = 0; i < 4; i++)
1168                                                         vertices[i].Pos.rotateXZBy(-44 + rotate_degree);
1169                                         }
1170                                 } else {
1171                                         p2mesh = n.param2 & 0x7;
1172                                         switch (p2mesh) {
1173                                         case 0:
1174                                                 // x
1175                                                 if (j == 0) {
1176                                                         for (u16 i = 0; i < 4; i++)
1177                                                                 vertices[i].Pos.rotateXZBy(46);
1178                                                 } else if (j == 1) {
1179                                                         for (u16 i = 0; i < 4; i++)
1180                                                                 vertices[i].Pos.rotateXZBy(-44);
1181                                                 }
1182                                                 break;
1183                                         case 1:
1184                                                 // +
1185                                                 if (j == 0) {
1186                                                         for (u16 i = 0; i < 4; i++)
1187                                                                 vertices[i].Pos.rotateXZBy(91);
1188                                                 } else if (j == 1) {
1189                                                         for (u16 i = 0; i < 4; i++)
1190                                                                 vertices[i].Pos.rotateXZBy(1);
1191                                                 }
1192                                                 break;
1193                                         case 2:
1194                                                 // *
1195                                                 if (j == 0) {
1196                                                         for (u16 i = 0; i < 4; i++)
1197                                                                 vertices[i].Pos.rotateXZBy(121);
1198                                                 } else if (j == 1) {
1199                                                         for (u16 i = 0; i < 4; i++)
1200                                                                 vertices[i].Pos.rotateXZBy(241);
1201                                                 } else { // (j == 2)
1202                                                         for (u16 i = 0; i < 4; i++)
1203                                                                 vertices[i].Pos.rotateXZBy(1);
1204                                                 }
1205                                                 break;
1206                                         case 3:
1207                                                 // #
1208                                                 switch (j) {
1209                                                 case 0:
1210                                                         for (u16 i = 0; i < 4; i++) {
1211                                                                 vertices[i].Pos.rotateXZBy(1);
1212                                                                 vertices[i].Pos.Z += BS / 4;
1213                                                         }
1214                                                         break;
1215                                                 case 1:
1216                                                         for (u16 i = 0; i < 4; i++) {
1217                                                                 vertices[i].Pos.rotateXZBy(91);
1218                                                                 vertices[i].Pos.X += BS / 4;
1219                                                         }
1220                                                         break;
1221                                                 case 2:
1222                                                         for (u16 i = 0; i < 4; i++) {
1223                                                                 vertices[i].Pos.rotateXZBy(181);
1224                                                                 vertices[i].Pos.Z -= BS / 4;
1225                                                         }
1226                                                         break;
1227                                                 case 3:
1228                                                         for (u16 i = 0; i < 4; i++) {
1229                                                                 vertices[i].Pos.rotateXZBy(271);
1230                                                                 vertices[i].Pos.X -= BS / 4;
1231                                                         }
1232                                                         break;
1233                                                 }
1234                                                 break;
1235                                         case 4:
1236                                                 // outward leaning #-like
1237                                                 switch (j) {
1238                                                 case 0:
1239                                                         for (u16 i = 2; i < 4; i++)
1240                                                                 vertices[i].Pos.Z -= BS / 2;
1241                                                         for (u16 i = 0; i < 4; i++)
1242                                                                 vertices[i].Pos.rotateXZBy(1);
1243                                                         break;
1244                                                 case 1:
1245                                                         for (u16 i = 2; i < 4; i++)
1246                                                                 vertices[i].Pos.Z -= BS / 2;
1247                                                         for (u16 i = 0; i < 4; i++)
1248                                                                 vertices[i].Pos.rotateXZBy(91);
1249                                                         break;
1250                                                 case 2:
1251                                                         for (u16 i = 2; i < 4; i++)
1252                                                                 vertices[i].Pos.Z -= BS / 2;
1253                                                         for (u16 i = 0; i < 4; i++)
1254                                                                 vertices[i].Pos.rotateXZBy(181);
1255                                                         break;
1256                                                 case 3:
1257                                                         for (u16 i = 2; i < 4; i++)
1258                                                                 vertices[i].Pos.Z -= BS / 2;
1259                                                         for (u16 i = 0; i < 4; i++)
1260                                                                 vertices[i].Pos.rotateXZBy(271);
1261                                                         break;
1262                                                 }
1263                                                 break;
1264                                         }
1265                                 }
1266
1267                                 for (int i = 0; i < 4; i++) {
1268                                         vertices[i].Pos *= f.visual_scale;
1269                                         vertices[i].Pos.Y += BS/2 * (f.visual_scale - 1);
1270                                         vertices[i].Pos += intToFloat(p, BS);
1271                                         // move to a random spot to avoid moire
1272                                         if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x8) != 0)) {
1273                                                 vertices[i].Pos.X += random_offset_X;
1274                                                 vertices[i].Pos.Z += random_offset_Z;
1275                                         }
1276                                         // randomly move each face up/down
1277                                         if ((f.param_type_2 == CPT2_MESHOPTIONS) && ((n.param2 & 0x20) != 0)) {
1278                                                 PseudoRandom yrng(j | x<<16 | z<<8 | y<<24 );
1279                                                 vertices[i].Pos.Y -= BS * ((yrng.next() % 16 / 16.0) * 0.125);
1280                                         }
1281                                 }
1282
1283                                 u16 indices[] = {0, 1, 2, 2, 3, 0};
1284                                 // Add to mesh collector
1285                                 collector.append(tile, vertices, 4, indices, 6);
1286
1287                                 // stop adding faces for meshes with less than 4 faces
1288                                 if (f.param_type_2 == CPT2_MESHOPTIONS) {
1289                                         if (((p2mesh == 0) || (p2mesh == 1)) && (j == 1))
1290                                                 break;
1291                                         else if ((p2mesh == 2) && (j == 2))
1292                                                 break;
1293                                 } else if (j == 1) {
1294                                         break;
1295                                 }
1296
1297                         }
1298                 break;}
1299                 case NDT_FIRELIKE:
1300                 {
1301                         TileSpec tile = getNodeTileN(n, p, 0, data);
1302                         tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1303
1304                         u16 l = getInteriorLight(n, 1, nodedef);
1305                         video::SColor c = MapBlock_LightColor(255, l, f.light_source);
1306
1307                         float s = BS / 2 * f.visual_scale;
1308
1309                         content_t current = n.getContent();
1310                         content_t n2c;
1311                         MapNode n2;
1312                         v3s16 n2p;
1313
1314                         static const v3s16 dirs[6] = {
1315                                 v3s16( 0,  1,  0),
1316                                 v3s16( 0, -1,  0),
1317                                 v3s16( 1,  0,  0),
1318                                 v3s16(-1,  0,  0),
1319                                 v3s16( 0,  0,  1),
1320                                 v3s16( 0,  0, -1)
1321                         };
1322
1323                         int doDraw[6] = {0, 0, 0, 0, 0, 0};
1324
1325                         bool drawAllFaces = true;
1326
1327                         // Check for adjacent nodes
1328                         for (int i = 0; i < 6; i++) {
1329                                 n2p = blockpos_nodes + p + dirs[i];
1330                                 n2 = data->m_vmanip.getNodeNoEx(n2p);
1331                                 n2c = n2.getContent();
1332                                 if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
1333                                         doDraw[i] = 1;
1334                                         if (drawAllFaces)
1335                                                 drawAllFaces = false;
1336
1337                                 }
1338                         }
1339
1340                         for (int j = 0; j < 6; j++) {
1341
1342                                 video::S3DVertex vertices[4] = {
1343                                         video::S3DVertex(-s, -BS / 2,         0, 0, 0, 0, c, 0, 1),
1344                                         video::S3DVertex( s, -BS / 2,         0, 0, 0, 0, c, 1, 1),
1345                                         video::S3DVertex( s, -BS / 2 + s * 2, 0, 0, 0, 0, c, 1, 0),
1346                                         video::S3DVertex(-s, -BS / 2 + s * 2, 0, 0, 0, 0, c, 0, 0),
1347                                 };
1348
1349                                 // Calculate which faces should be drawn, (top or sides)
1350                                 if (j == 0 && (drawAllFaces ||
1351                                                 (doDraw[3] == 1 || doDraw[1] == 1))) {
1352                                         for (int i = 0; i < 4; i++) {
1353                                                 vertices[i].Pos.rotateXZBy(90);
1354                                                 vertices[i].Pos.rotateXYBy(-10);
1355                                                 vertices[i].Pos.X -= 4.0;
1356                                         }
1357                                 } else if (j == 1 && (drawAllFaces ||
1358                                                 (doDraw[5] == 1 || doDraw[1] == 1))) {
1359                                         for (int i = 0; i < 4; i++) {
1360                                                 vertices[i].Pos.rotateXZBy(180);
1361                                                 vertices[i].Pos.rotateYZBy(10);
1362                                                 vertices[i].Pos.Z -= 4.0;
1363                                         }
1364                                 } else if (j == 2 && (drawAllFaces ||
1365                                                 (doDraw[2] == 1 || doDraw[1] == 1))) {
1366                                         for (int i = 0; i < 4; i++) {
1367                                                 vertices[i].Pos.rotateXZBy(270);
1368                                                 vertices[i].Pos.rotateXYBy(10);
1369                                                 vertices[i].Pos.X += 4.0;
1370                                         }
1371                                 } else if (j == 3 && (drawAllFaces ||
1372                                                 (doDraw[4] == 1 || doDraw[1] == 1))) {
1373                                         for (int i = 0; i < 4; i++) {
1374                                                 vertices[i].Pos.rotateYZBy(-10);
1375                                                 vertices[i].Pos.Z += 4.0;
1376                                         }
1377                                 // Center cross-flames
1378                                 } else if (j == 4 && (drawAllFaces || doDraw[1] == 1)) {
1379                                         for (int i = 0; i < 4; i++) {
1380                                                 vertices[i].Pos.rotateXZBy(45);
1381                                         }
1382                                 } else if (j == 5 && (drawAllFaces || doDraw[1] == 1)) {
1383                                         for (int i = 0; i < 4; i++) {
1384                                                 vertices[i].Pos.rotateXZBy(-45);
1385                                         }
1386                                 // Render flames on bottom of node above
1387                                 } else if (j == 0 && doDraw[0] == 1 && doDraw[1] == 0) {
1388                                         for (int i = 0; i < 4; i++) {
1389                                                 vertices[i].Pos.rotateYZBy(70);
1390                                                 vertices[i].Pos.rotateXZBy(90);
1391                                                 vertices[i].Pos.Y += 4.84;
1392                                                 vertices[i].Pos.X -= 4.7;
1393                                         }
1394                                 } else if (j == 1 && doDraw[0] == 1 && doDraw[1] == 0) {
1395                                         for (int i = 0; i < 4; i++) {
1396                                                 vertices[i].Pos.rotateYZBy(70);
1397                                                 vertices[i].Pos.rotateXZBy(180);
1398                                                 vertices[i].Pos.Y += 4.84;
1399                                                 vertices[i].Pos.Z -= 4.7;
1400                                         }
1401                                 } else if (j == 2 && doDraw[0] == 1 && doDraw[1] == 0) {
1402                                         for (int i = 0; i < 4; i++) {
1403                                                 vertices[i].Pos.rotateYZBy(70);
1404                                                 vertices[i].Pos.rotateXZBy(270);
1405                                                 vertices[i].Pos.Y += 4.84;
1406                                                 vertices[i].Pos.X += 4.7;
1407                                         }
1408                                 } else if (j == 3 && doDraw[0] == 1 && doDraw[1] == 0) {
1409                                         for (int i = 0; i < 4; i++) {
1410                                                 vertices[i].Pos.rotateYZBy(70);
1411                                                 vertices[i].Pos.Y += 4.84;
1412                                                 vertices[i].Pos.Z += 4.7;
1413                                         }
1414                                 } else {
1415                                         // Skip faces that aren't adjacent to a node
1416                                         continue;
1417                                 }
1418
1419                                 for (int i = 0; i < 4; i++) {
1420                                         vertices[i].Pos *= f.visual_scale;
1421                                         vertices[i].Pos += intToFloat(p, BS);
1422                                 }
1423
1424                                 u16 indices[] = {0, 1, 2, 2, 3, 0};
1425                                 // Add to mesh collector
1426                                 collector.append(tile, vertices, 4, indices, 6);
1427                         }
1428                 break;}
1429                 case NDT_FENCELIKE:
1430                 {
1431                         TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data);
1432                         TileSpec tile_nocrack = tile;
1433                         tile_nocrack.material_flags &= ~MATERIAL_FLAG_CRACK;
1434
1435                         // Put wood the right way around in the posts
1436                         TileSpec tile_rot = tile;
1437                         tile_rot.rotation = 1;
1438
1439                         u16 l = getInteriorLight(n, 1, nodedef);
1440                         video::SColor c = MapBlock_LightColor(255, l, f.light_source);
1441
1442                         const f32 post_rad=(f32)BS/8;
1443                         const f32 bar_rad=(f32)BS/16;
1444                         const f32 bar_len=(f32)(BS/2)-post_rad;
1445
1446                         v3f pos = intToFloat(p, BS);
1447
1448                         // The post - always present
1449                         aabb3f post(-post_rad,-BS/2,-post_rad,post_rad,BS/2,post_rad);
1450                         post.MinEdge += pos;
1451                         post.MaxEdge += pos;
1452                         f32 postuv[24]={
1453                                         6/16.,6/16.,10/16.,10/16.,
1454                                         6/16.,6/16.,10/16.,10/16.,
1455                                         0/16.,0,4/16.,1,
1456                                         4/16.,0,8/16.,1,
1457                                         8/16.,0,12/16.,1,
1458                                         12/16.,0,16/16.,1};
1459                         makeCuboid(&collector, post, &tile_rot, 1, c, postuv);
1460
1461                         // Now a section of fence, +X, if there's a post there
1462                         v3s16 p2 = p;
1463                         p2.X++;
1464                         MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1465                         const ContentFeatures *f2 = &nodedef->get(n2);
1466                         if(f2->drawtype == NDT_FENCELIKE)
1467                         {
1468                                 aabb3f bar(-bar_len+BS/2,-bar_rad+BS/4,-bar_rad,
1469                                                 bar_len+BS/2,bar_rad+BS/4,bar_rad);
1470                                 bar.MinEdge += pos;
1471                                 bar.MaxEdge += pos;
1472                                 f32 xrailuv[24]={
1473                                         0/16.,2/16.,16/16.,4/16.,
1474                                         0/16.,4/16.,16/16.,6/16.,
1475                                         6/16.,6/16.,8/16.,8/16.,
1476                                         10/16.,10/16.,12/16.,12/16.,
1477                                         0/16.,8/16.,16/16.,10/16.,
1478                                         0/16.,14/16.,16/16.,16/16.};
1479                                 makeCuboid(&collector, bar, &tile_nocrack, 1,
1480                                                 c, xrailuv);
1481                                 bar.MinEdge.Y -= BS/2;
1482                                 bar.MaxEdge.Y -= BS/2;
1483                                 makeCuboid(&collector, bar, &tile_nocrack, 1,
1484                                                 c, xrailuv);
1485                         }
1486
1487                         // Now a section of fence, +Z, if there's a post there
1488                         p2 = p;
1489                         p2.Z++;
1490                         n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1491                         f2 = &nodedef->get(n2);
1492                         if(f2->drawtype == NDT_FENCELIKE)
1493                         {
1494                                 aabb3f bar(-bar_rad,-bar_rad+BS/4,-bar_len+BS/2,
1495                                                 bar_rad,bar_rad+BS/4,bar_len+BS/2);
1496                                 bar.MinEdge += pos;
1497                                 bar.MaxEdge += pos;
1498                                 f32 zrailuv[24]={
1499                                         3/16.,1/16.,5/16.,5/16., // cannot rotate; stretch
1500                                         4/16.,1/16.,6/16.,5/16., // for wood texture instead
1501                                         0/16.,9/16.,16/16.,11/16.,
1502                                         0/16.,6/16.,16/16.,8/16.,
1503                                         6/16.,6/16.,8/16.,8/16.,
1504                                         10/16.,10/16.,12/16.,12/16.};
1505                                 makeCuboid(&collector, bar, &tile_nocrack, 1,
1506                                                 c, zrailuv);
1507                                 bar.MinEdge.Y -= BS/2;
1508                                 bar.MaxEdge.Y -= BS/2;
1509                                 makeCuboid(&collector, bar, &tile_nocrack, 1,
1510                                                 c, zrailuv);
1511                         }
1512                 break;}
1513                 case NDT_RAILLIKE:
1514                 {
1515                         bool is_rail_x[6]; /* (-1,-1,0) X (1,-1,0) (-1,0,0) X (1,0,0) (-1,1,0) X (1,1,0) */
1516                         bool is_rail_z[6];
1517
1518                         content_t thiscontent = n.getContent();
1519                         std::string groupname = "connect_to_raillike"; // name of the group that enables connecting to raillike nodes of different kind
1520                         int self_group = ((ItemGroupList) nodedef->get(n).groups)[groupname];
1521
1522                         u8 index = 0;
1523                         for (s8 y0 = -1; y0 <= 1; y0++) {
1524                                 // Prevent from indexing never used coordinates
1525                                 for (s8 xz = -1; xz <= 1; xz++) {
1526                                         if (xz == 0)
1527                                                 continue;
1528                                         MapNode n_xy = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x + xz, y + y0, z));
1529                                         MapNode n_zy = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y + y0, z + xz));
1530                                         const ContentFeatures &def_xy = nodedef->get(n_xy);
1531                                         const ContentFeatures &def_zy = nodedef->get(n_zy);
1532
1533                                         // Check if current node would connect with the rail
1534                                         is_rail_x[index] = ((def_xy.drawtype == NDT_RAILLIKE
1535                                                         && ((ItemGroupList) def_xy.groups)[groupname] == self_group)
1536                                                         || n_xy.getContent() == thiscontent);
1537
1538                                         is_rail_z[index] = ((def_zy.drawtype == NDT_RAILLIKE
1539                                                         && ((ItemGroupList) def_zy.groups)[groupname] == self_group)
1540                                                         || n_zy.getContent() == thiscontent);
1541                                         index++;
1542                                 }
1543                         }
1544
1545                         bool is_rail_x_all[2]; // [0] = negative x, [1] = positive x coordinate from the current node position
1546                         bool is_rail_z_all[2];
1547                         is_rail_x_all[0] = is_rail_x[0] || is_rail_x[2] || is_rail_x[4];
1548                         is_rail_x_all[1] = is_rail_x[1] || is_rail_x[3] || is_rail_x[5];
1549                         is_rail_z_all[0] = is_rail_z[0] || is_rail_z[2] || is_rail_z[4];
1550                         is_rail_z_all[1] = is_rail_z[1] || is_rail_z[3] || is_rail_z[5];
1551
1552                         // reasonable default, flat straight unrotated rail
1553                         bool is_straight = true;
1554                         int adjacencies = 0;
1555                         int angle = 0;
1556                         u8 tileindex = 0;
1557
1558                         // check for sloped rail
1559                         if (is_rail_x[4] || is_rail_x[5] || is_rail_z[4] || is_rail_z[5]) {
1560                                 adjacencies = 5; // 5 means sloped
1561                                 is_straight = true; // sloped is always straight
1562                         } else {
1563                                 // is really straight, rails on both sides
1564                                 is_straight = (is_rail_x_all[0] && is_rail_x_all[1]) || (is_rail_z_all[0] && is_rail_z_all[1]);
1565                                 adjacencies = is_rail_x_all[0] + is_rail_x_all[1] + is_rail_z_all[0] + is_rail_z_all[1];
1566                         }
1567
1568                         switch (adjacencies) {
1569                         case 1:
1570                                 if (is_rail_x_all[0] || is_rail_x_all[1])
1571                                         angle = 90;
1572                                 break;
1573                         case 2:
1574                                 if (!is_straight)
1575                                         tileindex = 1; // curved
1576                                 if (is_rail_x_all[0] && is_rail_x_all[1])
1577                                         angle = 90;
1578                                 if (is_rail_z_all[0] && is_rail_z_all[1]) {
1579                                         if (is_rail_z[4])
1580                                                 angle = 180;
1581                                 }
1582                                 else if (is_rail_x_all[0] && is_rail_z_all[0])
1583                                         angle = 270;
1584                                 else if (is_rail_x_all[0] && is_rail_z_all[1])
1585                                         angle = 180;
1586                                 else if (is_rail_x_all[1] && is_rail_z_all[1])
1587                                         angle = 90;
1588                                 break;
1589                         case 3:
1590                                 // here is where the potential to 'switch' a junction is, but not implemented at present
1591                                 tileindex = 2; // t-junction
1592                                 if(!is_rail_x_all[1])
1593                                         angle = 180;
1594                                 if(!is_rail_z_all[0])
1595                                         angle = 90;
1596                                 if(!is_rail_z_all[1])
1597                                         angle = 270;
1598                                 break;
1599                         case 4:
1600                                 tileindex = 3; // crossing
1601                                 break;
1602                         case 5: //sloped
1603                                 if (is_rail_z[4])
1604                                         angle = 180;
1605                                 if (is_rail_x[4])
1606                                         angle = 90;
1607                                 if (is_rail_x[5])
1608                                         angle = -90;
1609                                 break;
1610                         default:
1611                                 break;
1612                         }
1613
1614                         TileSpec tile = getNodeTileN(n, p, tileindex, data);
1615                         tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
1616                         tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1617
1618                         u16 l = getInteriorLight(n, 0, nodedef);
1619                         video::SColor c = MapBlock_LightColor(255, l, f.light_source);
1620
1621                         float d = (float)BS/64;
1622                         float s = BS/2;
1623
1624                         short g = -1;
1625                         if (is_rail_x[4] || is_rail_x[5] || is_rail_z[4] || is_rail_z[5])
1626                                 g = 1; //Object is at a slope
1627
1628                         video::S3DVertex vertices[4] =
1629                         {
1630                                         video::S3DVertex(-s,  -s+d,-s,  0,0,0,  c,0,1),
1631                                         video::S3DVertex( s,  -s+d,-s,  0,0,0,  c,1,1),
1632                                         video::S3DVertex( s, g*s+d, s,  0,0,0,  c,1,0),
1633                                         video::S3DVertex(-s, g*s+d, s,  0,0,0,  c,0,0),
1634                         };
1635
1636                         for(s32 i=0; i<4; i++)
1637                         {
1638                                 if(angle != 0)
1639                                         vertices[i].Pos.rotateXZBy(angle);
1640                                 vertices[i].Pos += intToFloat(p, BS);
1641                         }
1642
1643                         u16 indices[] = {0,1,2,2,3,0};
1644                         collector.append(tile, vertices, 4, indices, 6);
1645                 break;}
1646                 case NDT_NODEBOX:
1647                 {
1648                         static const v3s16 tile_dirs[6] = {
1649                                 v3s16(0, 1, 0),
1650                                 v3s16(0, -1, 0),
1651                                 v3s16(1, 0, 0),
1652                                 v3s16(-1, 0, 0),
1653                                 v3s16(0, 0, 1),
1654                                 v3s16(0, 0, -1)
1655                         };
1656                         TileSpec tiles[6];
1657
1658                         u16 l = getInteriorLight(n, 1, nodedef);
1659                         video::SColor c = MapBlock_LightColor(255, l, f.light_source);
1660
1661                         v3f pos = intToFloat(p, BS);
1662
1663                         int neighbors = 0;
1664
1665                         // locate possible neighboring nodes to connect to
1666                         if (f.node_box.type == NODEBOX_CONNECTED) {
1667                                 v3s16 p2 = p;
1668
1669                                 p2.Y++;
1670                                 getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 1, &neighbors);
1671
1672                                 p2 = p;
1673                                 p2.Y--;
1674                                 getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 2, &neighbors);
1675
1676                                 p2 = p;
1677                                 p2.Z--;
1678                                 getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 4, &neighbors);
1679
1680                                 p2 = p;
1681                                 p2.X--;
1682                                 getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 8, &neighbors);
1683
1684                                 p2 = p;
1685                                 p2.Z++;
1686                                 getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 16, &neighbors);
1687
1688                                 p2 = p;
1689                                 p2.X++;
1690                                 getNeighborConnectingFace(blockpos_nodes + p2, nodedef, data, n, 32, &neighbors);
1691                         }
1692
1693                         std::vector<aabb3f> boxes;
1694                         n.getNodeBoxes(nodedef, &boxes, neighbors);
1695                         for(std::vector<aabb3f>::iterator
1696                                         i = boxes.begin();
1697                                         i != boxes.end(); ++i)
1698                         {
1699                         for(int j = 0; j < 6; j++)
1700                                 {
1701                                 // Handles facedir rotation for textures
1702                                 tiles[j] = getNodeTile(n, p, tile_dirs[j], data);
1703                                 }
1704                                 aabb3f box = *i;
1705                                 box.MinEdge += pos;
1706                                 box.MaxEdge += pos;
1707
1708                                 f32 temp;
1709                                 if (box.MinEdge.X > box.MaxEdge.X)
1710                                 {
1711                                         temp=box.MinEdge.X;
1712                                         box.MinEdge.X=box.MaxEdge.X;
1713                                         box.MaxEdge.X=temp;
1714                                 }
1715                                 if (box.MinEdge.Y > box.MaxEdge.Y)
1716                                 {
1717                                         temp=box.MinEdge.Y;
1718                                         box.MinEdge.Y=box.MaxEdge.Y;
1719                                         box.MaxEdge.Y=temp;
1720                                 }
1721                                 if (box.MinEdge.Z > box.MaxEdge.Z)
1722                                 {
1723                                         temp=box.MinEdge.Z;
1724                                         box.MinEdge.Z=box.MaxEdge.Z;
1725                                         box.MaxEdge.Z=temp;
1726                                 }
1727
1728                                 //
1729                                 // Compute texture coords
1730                                 f32 tx1 = (box.MinEdge.X/BS)+0.5;
1731                                 f32 ty1 = (box.MinEdge.Y/BS)+0.5;
1732                                 f32 tz1 = (box.MinEdge.Z/BS)+0.5;
1733                                 f32 tx2 = (box.MaxEdge.X/BS)+0.5;
1734                                 f32 ty2 = (box.MaxEdge.Y/BS)+0.5;
1735                                 f32 tz2 = (box.MaxEdge.Z/BS)+0.5;
1736                                 f32 txc[24] = {
1737                                         // up
1738                                         tx1, 1-tz2, tx2, 1-tz1,
1739                                         // down
1740                                         tx1, tz1, tx2, tz2,
1741                                         // right
1742                                         tz1, 1-ty2, tz2, 1-ty1,
1743                                         // left
1744                                         1-tz2, 1-ty2, 1-tz1, 1-ty1,
1745                                         // back
1746                                         1-tx2, 1-ty2, 1-tx1, 1-ty1,
1747                                         // front
1748                                         tx1, 1-ty2, tx2, 1-ty1,
1749                                 };
1750                                 makeCuboid(&collector, box, tiles, 6, c, txc);
1751                         }
1752                 break;}
1753                 case NDT_MESH:
1754                 {
1755                         v3f pos = intToFloat(p, BS);
1756                         video::SColor c = MapBlock_LightColor(255, getInteriorLight(n, 1, nodedef), f.light_source);
1757
1758                         u8 facedir = 0;
1759                         if (f.param_type_2 == CPT2_FACEDIR) {
1760                                 facedir = n.getFaceDir(nodedef);
1761                         } else if (f.param_type_2 == CPT2_WALLMOUNTED) {
1762                                 //convert wallmounted to 6dfacedir.
1763                                 //when cache enabled, it is already converted
1764                                 facedir = n.getWallMounted(nodedef);
1765                                 if (!enable_mesh_cache) {
1766                                         static const u8 wm_to_6d[6] = {20, 0, 16+1, 12+3, 8, 4+2};
1767                                         facedir = wm_to_6d[facedir];
1768                                 }
1769                         }
1770
1771                         if (f.mesh_ptr[facedir]) {
1772                                 // use cached meshes
1773                                 for(u16 j = 0; j < f.mesh_ptr[0]->getMeshBufferCount(); j++) {
1774                                         scene::IMeshBuffer *buf = f.mesh_ptr[facedir]->getMeshBuffer(j);
1775                                         collector.append(getNodeTileN(n, p, j, data),
1776                                                 (video::S3DVertex *)buf->getVertices(), buf->getVertexCount(),
1777                                                 buf->getIndices(), buf->getIndexCount(), pos, c);
1778                                 }
1779                         } else if (f.mesh_ptr[0]) {
1780                                 // no cache, clone and rotate mesh
1781                                 scene::IMesh* mesh = cloneMesh(f.mesh_ptr[0]);
1782                                 rotateMeshBy6dFacedir(mesh, facedir);
1783                                 recalculateBoundingBox(mesh);
1784                                 meshmanip->recalculateNormals(mesh, true, false);
1785                                 for(u16 j = 0; j < mesh->getMeshBufferCount(); j++) {
1786                                         scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1787                                         collector.append(getNodeTileN(n, p, j, data),
1788                                                 (video::S3DVertex *)buf->getVertices(), buf->getVertexCount(),
1789                                                 buf->getIndices(), buf->getIndexCount(), pos, c);
1790                                 }
1791                                 mesh->drop();
1792                         }
1793                 break;}
1794                 }
1795         }
1796 }
1797