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