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