Node highlighting.
[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
22 #include "main.h" // For g_settings
23 #include "mapblock_mesh.h" // For MapBlock_LightColor() and MeshCollector
24 #include "settings.h"
25 #include "nodedef.h"
26 #include "tile.h"
27 #include "gamedef.h"
28 #include "util/numeric.h"
29 #include "util/directiontables.h"
30
31 // Create a cuboid.
32 //  collector - the MeshCollector for the resulting polygons
33 //  box       - the position and size of the box
34 //  tiles     - the tiles (materials) to use (for all 6 faces)
35 //  tilecount - number of entries in tiles, 1<=tilecount<=6
36 //  c         - vertex colour - used for all
37 //  txc       - texture coordinates - this is a list of texture coordinates
38 //              for the opposite corners of each face - therefore, there
39 //              should be (2+2)*6=24 values in the list. Alternatively, pass
40 //              NULL to use the entire texture for each face. The order of
41 //              the faces in the list is up-down-right-left-back-front
42 //              (compatible with ContentFeatures). If you specified 0,0,1,1
43 //              for each face, that would be the same as passing NULL.
44 void makeCuboid(MeshCollector *collector, const aabb3f &box,
45         TileSpec *tiles, int tilecount,
46         video::SColor &c, const f32* txc)
47 {
48         assert(tilecount >= 1 && tilecount <= 6);
49
50         v3f min = box.MinEdge;
51         v3f max = box.MaxEdge;
52  
53  
54  
55         if(txc == NULL)
56         {
57                 static const f32 txc_default[24] = {
58                         0,0,1,1,
59                         0,0,1,1,
60                         0,0,1,1,
61                         0,0,1,1,
62                         0,0,1,1,
63                         0,0,1,1
64                 };
65                 txc = txc_default;
66         }
67
68         video::S3DVertex vertices[24] =
69         {
70                 // up
71                 video::S3DVertex(min.X,max.Y,max.Z, 0,1,0, c, txc[0],txc[1]),
72                 video::S3DVertex(max.X,max.Y,max.Z, 0,1,0, c, txc[2],txc[1]),
73                 video::S3DVertex(max.X,max.Y,min.Z, 0,1,0, c, txc[2],txc[3]),
74                 video::S3DVertex(min.X,max.Y,min.Z, 0,1,0, c, txc[0],txc[3]),
75                 // down
76                 video::S3DVertex(min.X,min.Y,min.Z, 0,-1,0, c, txc[4],txc[5]),
77                 video::S3DVertex(max.X,min.Y,min.Z, 0,-1,0, c, txc[6],txc[5]),
78                 video::S3DVertex(max.X,min.Y,max.Z, 0,-1,0, c, txc[6],txc[7]),
79                 video::S3DVertex(min.X,min.Y,max.Z, 0,-1,0, c, txc[4],txc[7]),
80                 // right
81                 video::S3DVertex(max.X,max.Y,min.Z, 1,0,0, c, txc[ 8],txc[9]),
82                 video::S3DVertex(max.X,max.Y,max.Z, 1,0,0, c, txc[10],txc[9]),
83                 video::S3DVertex(max.X,min.Y,max.Z, 1,0,0, c, txc[10],txc[11]),
84                 video::S3DVertex(max.X,min.Y,min.Z, 1,0,0, c, txc[ 8],txc[11]),
85                 // left
86                 video::S3DVertex(min.X,max.Y,max.Z, -1,0,0, c, txc[12],txc[13]),
87                 video::S3DVertex(min.X,max.Y,min.Z, -1,0,0, c, txc[14],txc[13]),
88                 video::S3DVertex(min.X,min.Y,min.Z, -1,0,0, c, txc[14],txc[15]),
89                 video::S3DVertex(min.X,min.Y,max.Z, -1,0,0, c, txc[12],txc[15]),
90                 // back
91                 video::S3DVertex(max.X,max.Y,max.Z, 0,0,1, c, txc[16],txc[17]),
92                 video::S3DVertex(min.X,max.Y,max.Z, 0,0,1, c, txc[18],txc[17]),
93                 video::S3DVertex(min.X,min.Y,max.Z, 0,0,1, c, txc[18],txc[19]),
94                 video::S3DVertex(max.X,min.Y,max.Z, 0,0,1, c, txc[16],txc[19]),
95                 // front
96                 video::S3DVertex(min.X,max.Y,min.Z, 0,0,-1, c, txc[20],txc[21]),
97                 video::S3DVertex(max.X,max.Y,min.Z, 0,0,-1, c, txc[22],txc[21]),
98                 video::S3DVertex(max.X,min.Y,min.Z, 0,0,-1, c, txc[22],txc[23]),
99                 video::S3DVertex(min.X,min.Y,min.Z, 0,0,-1, c, txc[20],txc[23]),
100         };
101
102         for(int i = 0; i < 6; i++)
103                                 {
104                                 switch (tiles[MYMIN(i, tilecount-1)].rotation)
105                                 {
106                                 case 0:
107                                         break;
108                                 case 1: //R90
109                                         for (int x = 0; x < 4; x++)
110                                                 vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
111                                         break;
112                                 case 2: //R180
113                                         for (int x = 0; x < 4; x++)
114                                                 vertices[i*4+x].TCoords.rotateBy(180,irr::core::vector2df(0, 0));
115                                         break;
116                                 case 3: //R270
117                                         for (int x = 0; x < 4; x++)
118                                                 vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
119                                         break;
120                                 case 4: //FXR90
121                                         for (int x = 0; x < 4; x++){
122                                                 vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X;
123                                                 vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
124                                         }
125                                         break;
126                                 case 5: //FXR270
127                                         for (int x = 0; x < 4; x++){
128                                                 vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X;
129                                                 vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
130                                         }
131                                         break;
132                                 case 6: //FYR90
133                                         for (int x = 0; x < 4; x++){
134                                                 vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y;
135                                                 vertices[i*4+x].TCoords.rotateBy(90,irr::core::vector2df(0, 0));
136                                         }
137                                         break;
138                                 case 7: //FYR270
139                                         for (int x = 0; x < 4; x++){
140                                                 vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y;
141                                                 vertices[i*4+x].TCoords.rotateBy(270,irr::core::vector2df(0, 0));
142                                         }
143                                         break;
144                                 case 8: //FX
145                                         for (int x = 0; x < 4; x++){
146                                                 vertices[i*4+x].TCoords.X = 1.0 - vertices[i*4+x].TCoords.X;
147                                         }
148                                         break;
149                                 case 9: //FY
150                                         for (int x = 0; x < 4; x++){
151                                                 vertices[i*4+x].TCoords.Y = 1.0 - vertices[i*4+x].TCoords.Y;
152                                         }
153                                         break;
154                                 default:
155                                         break;
156                                 }
157                         }
158         u16 indices[] = {0,1,2,2,3,0};
159         // Add to mesh collector
160         for(s32 j=0; j<24; j+=4)
161         {
162                 int tileindex = MYMIN(j/4, tilecount-1);
163                 collector->append(tiles[tileindex],
164                                 vertices+j, 4, indices, 6);
165         }
166 }
167
168 void mapblock_mesh_generate_special(MeshMakeData *data,
169                 MeshCollector &collector)
170 {
171         INodeDefManager *nodedef = data->m_gamedef->ndef();
172         ITextureSource *tsrc = data->m_gamedef->tsrc();
173
174         // 0ms
175         //TimeTaker timer("mapblock_mesh_generate_special()");
176
177         /*
178                 Some settings
179         */
180         bool new_style_water = g_settings->getBool("new_style_water");
181
182         float node_liquid_level = 1.0;
183         if (new_style_water)
184                 node_liquid_level = 0.85;
185
186         v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
187
188         // Create selection mesh
189         v3s16 p = data->m_highlighted_pos_relative;
190         if (data->m_show_hud & 
191                         (p.X >= 0) & (p.X < MAP_BLOCKSIZE) &
192                         (p.Y >= 0) & (p.Y < MAP_BLOCKSIZE) &
193                         (p.Z >= 0) & (p.Z < MAP_BLOCKSIZE)) {
194
195                 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
196                 // Get selection mesh light level
197                 static const v3s16 dirs[7] = {
198                                 v3s16( 0, 0, 0),
199                                 v3s16( 0, 1, 0),
200                                 v3s16( 0,-1, 0),
201                                 v3s16( 1, 0, 0),
202                                 v3s16(-1, 0, 0),
203                                 v3s16( 0, 0, 1),
204                                 v3s16( 0, 0,-1)
205                 };
206
207                 u16 l = 0;
208                 u16 l1 = 0;
209                 for (u8 i = 0; i < 7; i++) {
210                         MapNode n1 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dirs[i]);  
211                         l1 = getInteriorLight(n1, -4, nodedef);
212                         if (l1 > l) 
213                                 l = l1;
214                 }
215                 video::SColor c = MapBlock_LightColor(255, l, 0);
216                 data->m_highlight_mesh_color = c;       
217                 std::vector<aabb3f> boxes = n.getSelectionBoxes(nodedef);
218                 TileSpec h_tile;                        
219                 h_tile.material_flags |= MATERIAL_FLAG_HIGHLIGHTED;
220                 h_tile.texture = tsrc->getTexture("halo.png",&h_tile.texture_id);
221                 v3f pos = intToFloat(p, BS);
222                 f32 d = 0.05 * BS;
223                 for(std::vector<aabb3f>::iterator
224                                 i = boxes.begin();
225                                 i != boxes.end(); i++)
226                 {
227                         aabb3f box = *i;
228                         box.MinEdge += v3f(-d, -d, -d) + pos;
229                         box.MaxEdge += v3f(d, d, d) + pos;
230                         makeCuboid(&collector, box, &h_tile, 1, c, NULL);
231                 }
232         }
233
234         for(s16 z = 0; z < MAP_BLOCKSIZE; z++)
235         for(s16 y = 0; y < MAP_BLOCKSIZE; y++)
236         for(s16 x = 0; x < MAP_BLOCKSIZE; x++)
237         {
238                 v3s16 p(x,y,z);
239
240                 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
241                 const ContentFeatures &f = nodedef->get(n);
242
243                 // Only solidness=0 stuff is drawn here
244                 if(f.solidness != 0)
245                         continue;
246
247                 switch(f.drawtype){
248                 default:
249                         infostream<<"Got "<<f.drawtype<<std::endl;
250                         assert(0);
251                         break;
252                 case NDT_AIRLIKE:
253                         break;
254                 case NDT_LIQUID:
255                 {
256                         /*
257                                 Add water sources to mesh if using new style
258                         */
259                         TileSpec tile_liquid = f.special_tiles[0];
260                         TileSpec tile_liquid_bfculled = getNodeTile(n, p, v3s16(0,0,0), data);
261
262                         bool top_is_same_liquid = false;
263                         MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
264                         content_t c_flowing = nodedef->getId(f.liquid_alternative_flowing);
265                         content_t c_source = nodedef->getId(f.liquid_alternative_source);
266                         if(ntop.getContent() == c_flowing || ntop.getContent() == c_source)
267                                 top_is_same_liquid = true;
268
269                         u16 l = getInteriorLight(n, 0, nodedef);
270                         video::SColor c = MapBlock_LightColor(f.alpha, l, f.light_source);
271
272                         /*
273                                 Generate sides
274                          */
275                         v3s16 side_dirs[4] = {
276                                 v3s16(1,0,0),
277                                 v3s16(-1,0,0),
278                                 v3s16(0,0,1),
279                                 v3s16(0,0,-1),
280                         };
281                         for(u32 i=0; i<4; i++)
282                         {
283                                 v3s16 dir = side_dirs[i];
284
285                                 MapNode neighbor = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
286                                 content_t neighbor_content = neighbor.getContent();
287                                 const ContentFeatures &n_feat = nodedef->get(neighbor_content);
288                                 MapNode n_top = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir+ v3s16(0,1,0));
289                                 content_t n_top_c = n_top.getContent();
290
291                                 if(neighbor_content == CONTENT_IGNORE)
292                                         continue;
293
294                                 /*
295                                         If our topside is liquid and neighbor's topside
296                                         is liquid, don't draw side face
297                                 */
298                                 if(top_is_same_liquid && (n_top_c == c_flowing ||
299                                                 n_top_c == c_source || n_top_c == CONTENT_IGNORE))
300                                         continue;
301
302                                 // Don't draw face if neighbor is blocking the view
303                                 if(n_feat.solidness == 2)
304                                         continue;
305
306                                 bool neighbor_is_same_liquid = (neighbor_content == c_source
307                                                 || neighbor_content == c_flowing);
308
309                                 // Don't draw any faces if neighbor same is liquid and top is
310                                 // same liquid
311                                 if(neighbor_is_same_liquid && !top_is_same_liquid)
312                                         continue;
313
314                                 // Use backface culled material if neighbor doesn't have a
315                                 // solidness of 0
316                                 const TileSpec *current_tile = &tile_liquid;
317                                 if(n_feat.solidness != 0 || n_feat.visual_solidness != 0)
318                                         current_tile = &tile_liquid_bfculled;
319
320                                 video::S3DVertex vertices[4] =
321                                 {
322                                         video::S3DVertex(-BS/2,0,BS/2,0,0,0, c, 0,1),
323                                         video::S3DVertex(BS/2,0,BS/2,0,0,0, c, 1,1),
324                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
325                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
326                                 };
327
328                                 /*
329                                         If our topside is liquid, set upper border of face
330                                         at upper border of node
331                                 */
332                                 if(top_is_same_liquid)
333                                 {
334                                         vertices[2].Pos.Y = 0.5*BS;
335                                         vertices[3].Pos.Y = 0.5*BS;
336                                 }
337                                 /*
338                                         Otherwise upper position of face is liquid level
339                                 */
340                                 else
341                                 {
342                                         vertices[2].Pos.Y = (node_liquid_level-0.5)*BS;
343                                         vertices[3].Pos.Y = (node_liquid_level-0.5)*BS;
344                                 }
345                                 /*
346                                         If neighbor is liquid, lower border of face is liquid level
347                                 */
348                                 if(neighbor_is_same_liquid)
349                                 {
350                                         vertices[0].Pos.Y = (node_liquid_level-0.5)*BS;
351                                         vertices[1].Pos.Y = (node_liquid_level-0.5)*BS;
352                                 }
353                                 /*
354                                         If neighbor is not liquid, lower border of face is
355                                         lower border of node
356                                 */
357                                 else
358                                 {
359                                         vertices[0].Pos.Y = -0.5*BS;
360                                         vertices[1].Pos.Y = -0.5*BS;
361                                 }
362
363                                 for(s32 j=0; j<4; j++)
364                                 {
365                                         if(dir == v3s16(0,0,1))
366                                                 vertices[j].Pos.rotateXZBy(0);
367                                         if(dir == v3s16(0,0,-1))
368                                                 vertices[j].Pos.rotateXZBy(180);
369                                         if(dir == v3s16(-1,0,0))
370                                                 vertices[j].Pos.rotateXZBy(90);
371                                         if(dir == v3s16(1,0,-0))
372                                                 vertices[j].Pos.rotateXZBy(-90);
373
374                                         // Do this to not cause glitches when two liquids are
375                                         // side-by-side
376                                         /*if(neighbor_is_same_liquid == false){
377                                                 vertices[j].Pos.X *= 0.98;
378                                                 vertices[j].Pos.Z *= 0.98;
379                                         }*/
380
381                                         vertices[j].Pos += intToFloat(p, BS);
382                                 }
383
384                                 u16 indices[] = {0,1,2,2,3,0};
385                                 // Add to mesh collector
386                                 collector.append(*current_tile, vertices, 4, indices, 6);
387                         }
388
389                         /*
390                                 Generate top
391                          */
392                         if(top_is_same_liquid)
393                                 continue;
394                         
395                         video::S3DVertex vertices[4] =
396                         {
397                                 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
398                                 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
399                                 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,0),
400                                 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,0),
401                         };
402
403                         v3f offset(p.X*BS, p.Y*BS + (-0.5+node_liquid_level)*BS, p.Z*BS);
404                         for(s32 i=0; i<4; i++)
405                         {
406                                 vertices[i].Pos += offset;
407                         }
408
409                         u16 indices[] = {0,1,2,2,3,0};
410                         // Add to mesh collector
411                         collector.append(tile_liquid, vertices, 4, indices, 6);
412                 break;}
413                 case NDT_FLOWINGLIQUID:
414                 {
415                         /*
416                                 Add flowing liquid to mesh
417                         */
418                         TileSpec tile_liquid = f.special_tiles[0];
419                         TileSpec tile_liquid_bfculled = f.special_tiles[1];
420
421                         bool top_is_same_liquid = false;
422                         MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
423                         content_t c_flowing = nodedef->getId(f.liquid_alternative_flowing);
424                         content_t c_source = nodedef->getId(f.liquid_alternative_source);
425                         if(ntop.getContent() == c_flowing || ntop.getContent() == c_source)
426                                 top_is_same_liquid = true;
427                         
428                         u16 l = 0;
429                         // If this liquid emits light and doesn't contain light, draw
430                         // it at what it emits, for an increased effect
431                         u8 light_source = nodedef->get(n).light_source;
432                         if(light_source != 0){
433                                 l = decode_light(light_source);
434                                 l = l | (l<<8);
435                         }
436                         // Use the light of the node on top if possible
437                         else if(nodedef->get(ntop).param_type == CPT_LIGHT)
438                                 l = getInteriorLight(ntop, 0, nodedef);
439                         // Otherwise use the light of this node (the liquid)
440                         else
441                                 l = getInteriorLight(n, 0, nodedef);
442                         video::SColor c = MapBlock_LightColor(f.alpha, l, f.light_source);
443                         
444                         u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8);
445
446                         // Neighbor liquid levels (key = relative position)
447                         // Includes current node
448                         std::map<v3s16, f32> neighbor_levels;
449                         std::map<v3s16, content_t> neighbor_contents;
450                         std::map<v3s16, u8> neighbor_flags;
451                         const u8 neighborflag_top_is_same_liquid = 0x01;
452                         v3s16 neighbor_dirs[9] = {
453                                 v3s16(0,0,0),
454                                 v3s16(0,0,1),
455                                 v3s16(0,0,-1),
456                                 v3s16(1,0,0),
457                                 v3s16(-1,0,0),
458                                 v3s16(1,0,1),
459                                 v3s16(-1,0,-1),
460                                 v3s16(1,0,-1),
461                                 v3s16(-1,0,1),
462                         };
463                         for(u32 i=0; i<9; i++)
464                         {
465                                 content_t content = CONTENT_AIR;
466                                 float level = -0.5 * BS;
467                                 u8 flags = 0;
468                                 // Check neighbor
469                                 v3s16 p2 = p + neighbor_dirs[i];
470                                 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
471                                 if(n2.getContent() != CONTENT_IGNORE)
472                                 {
473                                         content = n2.getContent();
474
475                                         if(n2.getContent() == c_source)
476                                                 level = (-0.5+node_liquid_level) * BS;
477                                         else if(n2.getContent() == c_flowing){
478                                                 u8 liquid_level = (n2.param2&LIQUID_LEVEL_MASK);
479                                                 if (liquid_level <= LIQUID_LEVEL_MAX+1-range)
480                                                         liquid_level = 0;
481                                                 else
482                                                         liquid_level -= (LIQUID_LEVEL_MAX+1-range);
483                                                 level = (-0.5 + ((float)liquid_level+ 0.5) / (float)range * node_liquid_level) * BS;
484                                         }
485
486                                         // Check node above neighbor.
487                                         // NOTE: This doesn't get executed if neighbor
488                                         //       doesn't exist
489                                         p2.Y += 1;
490                                         n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
491                                         if(n2.getContent() == c_source ||
492                                                         n2.getContent() == c_flowing)
493                                                 flags |= neighborflag_top_is_same_liquid;
494                                 }
495                                 
496                                 neighbor_levels[neighbor_dirs[i]] = level;
497                                 neighbor_contents[neighbor_dirs[i]] = content;
498                                 neighbor_flags[neighbor_dirs[i]] = flags;
499                         }
500
501                         // Corner heights (average between four liquids)
502                         f32 corner_levels[4];
503                         
504                         v3s16 halfdirs[4] = {
505                                 v3s16(0,0,0),
506                                 v3s16(1,0,0),
507                                 v3s16(1,0,1),
508                                 v3s16(0,0,1),
509                         };
510                         for(u32 i=0; i<4; i++)
511                         {
512                                 v3s16 cornerdir = halfdirs[i];
513                                 float cornerlevel = 0;
514                                 u32 valid_count = 0;
515                                 u32 air_count = 0;
516                                 for(u32 j=0; j<4; j++)
517                                 {
518                                         v3s16 neighbordir = cornerdir - halfdirs[j];
519                                         content_t content = neighbor_contents[neighbordir];
520                                         // If top is liquid, draw starting from top of node
521                                         if(neighbor_flags[neighbordir] &
522                                                         neighborflag_top_is_same_liquid)
523                                         {
524                                                 cornerlevel = 0.5*BS;
525                                                 valid_count = 1;
526                                                 break;
527                                         }
528                                         // Source is always the same height
529                                         else if(content == c_source)
530                                         {
531                                                 cornerlevel = (-0.5+node_liquid_level)*BS;
532                                                 valid_count = 1;
533                                                 break;
534                                         }
535                                         // Flowing liquid has level information
536                                         else if(content == c_flowing)
537                                         {
538                                                 cornerlevel += neighbor_levels[neighbordir];
539                                                 valid_count++;
540                                         }
541                                         else if(content == CONTENT_AIR)
542                                         {
543                                                 air_count++;
544                                         }
545                                 }
546                                 if(air_count >= 2)
547                                         cornerlevel = -0.5*BS+0.2;
548                                 else if(valid_count > 0)
549                                         cornerlevel /= valid_count;
550                                 corner_levels[i] = cornerlevel;
551                         }
552
553                         /*
554                                 Generate sides
555                         */
556
557                         v3s16 side_dirs[4] = {
558                                 v3s16(1,0,0),
559                                 v3s16(-1,0,0),
560                                 v3s16(0,0,1),
561                                 v3s16(0,0,-1),
562                         };
563                         s16 side_corners[4][2] = {
564                                 {1, 2},
565                                 {3, 0},
566                                 {2, 3},
567                                 {0, 1},
568                         };
569                         for(u32 i=0; i<4; i++)
570                         {
571                                 v3s16 dir = side_dirs[i];
572
573                                 /*
574                                         If our topside is liquid and neighbor's topside
575                                         is liquid, don't draw side face
576                                 */
577                                 if(top_is_same_liquid &&
578                                                 neighbor_flags[dir] & neighborflag_top_is_same_liquid)
579                                         continue;
580
581                                 content_t neighbor_content = neighbor_contents[dir];
582                                 const ContentFeatures &n_feat = nodedef->get(neighbor_content);
583                                 
584                                 // Don't draw face if neighbor is blocking the view
585                                 if(n_feat.solidness == 2)
586                                         continue;
587                                 
588                                 bool neighbor_is_same_liquid = (neighbor_content == c_source
589                                                 || neighbor_content == c_flowing);
590                                 
591                                 // Don't draw any faces if neighbor same is liquid and top is
592                                 // same liquid
593                                 if(neighbor_is_same_liquid == true
594                                                 && top_is_same_liquid == false)
595                                         continue;
596
597                                 // Use backface culled material if neighbor doesn't have a
598                                 // solidness of 0
599                                 const TileSpec *current_tile = &tile_liquid;
600                                 if(n_feat.solidness != 0 || n_feat.visual_solidness != 0)
601                                         current_tile = &tile_liquid_bfculled;
602                                 
603                                 video::S3DVertex vertices[4] =
604                                 {
605                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
606                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
607                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
608                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
609                                 };
610                                 
611                                 /*
612                                         If our topside is liquid, set upper border of face
613                                         at upper border of node
614                                 */
615                                 if(top_is_same_liquid)
616                                 {
617                                         vertices[2].Pos.Y = 0.5*BS;
618                                         vertices[3].Pos.Y = 0.5*BS;
619                                 }
620                                 /*
621                                         Otherwise upper position of face is corner levels
622                                 */
623                                 else
624                                 {
625                                         vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
626                                         vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
627                                 }
628                                 
629                                 /*
630                                         If neighbor is liquid, lower border of face is corner
631                                         liquid levels
632                                 */
633                                 if(neighbor_is_same_liquid)
634                                 {
635                                         vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
636                                         vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
637                                 }
638                                 /*
639                                         If neighbor is not liquid, lower border of face is
640                                         lower border of node
641                                 */
642                                 else
643                                 {
644                                         vertices[0].Pos.Y = -0.5*BS;
645                                         vertices[1].Pos.Y = -0.5*BS;
646                                 }
647                                 
648                                 for(s32 j=0; j<4; j++)
649                                 {
650                                         if(dir == v3s16(0,0,1))
651                                                 vertices[j].Pos.rotateXZBy(0);
652                                         if(dir == v3s16(0,0,-1))
653                                                 vertices[j].Pos.rotateXZBy(180);
654                                         if(dir == v3s16(-1,0,0))
655                                                 vertices[j].Pos.rotateXZBy(90);
656                                         if(dir == v3s16(1,0,-0))
657                                                 vertices[j].Pos.rotateXZBy(-90);
658                                                 
659                                         // Do this to not cause glitches when two liquids are
660                                         // side-by-side
661                                         /*if(neighbor_is_same_liquid == false){
662                                                 vertices[j].Pos.X *= 0.98;
663                                                 vertices[j].Pos.Z *= 0.98;
664                                         }*/
665
666                                         vertices[j].Pos += intToFloat(p, BS);
667                                 }
668
669                                 u16 indices[] = {0,1,2,2,3,0};
670                                 // Add to mesh collector
671                                 collector.append(*current_tile, vertices, 4, indices, 6);
672                         }
673                         
674                         /*
675                                 Generate top side, if appropriate
676                         */
677                         
678                         if(top_is_same_liquid == false)
679                         {
680                                 video::S3DVertex vertices[4] =
681                                 {
682                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
683                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
684                                         video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,0),
685                                         video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,0),
686                                 };
687                                 
688                                 // To get backface culling right, the vertices need to go
689                                 // clockwise around the front of the face. And we happened to
690                                 // calculate corner levels in exact reverse order.
691                                 s32 corner_resolve[4] = {3,2,1,0};
692
693                                 for(s32 i=0; i<4; i++)
694                                 {
695                                         //vertices[i].Pos.Y += liquid_level;
696                                         //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
697                                         s32 j = corner_resolve[i];
698                                         vertices[i].Pos.Y += corner_levels[j];
699                                         vertices[i].Pos += intToFloat(p, BS);
700                                 }
701                                 
702                                 // Default downwards-flowing texture animation goes from 
703                                 // -Z towards +Z, thus the direction is +Z.
704                                 // Rotate texture to make animation go in flow direction
705                                 // Positive if liquid moves towards +Z
706                                 f32 dz = (corner_levels[side_corners[3][0]] +
707                                                 corner_levels[side_corners[3][1]]) -
708                                                 (corner_levels[side_corners[2][0]] +
709                                                 corner_levels[side_corners[2][1]]);
710                                 // Positive if liquid moves towards +X
711                                 f32 dx = (corner_levels[side_corners[1][0]] +
712                                                 corner_levels[side_corners[1][1]]) -
713                                                 (corner_levels[side_corners[0][0]] +
714                                                 corner_levels[side_corners[0][1]]);
715                                 f32 tcoord_angle = atan2(dz, dx) * core::RADTODEG ;
716                                 v2f tcoord_center(0.5, 0.5);
717                                 v2f tcoord_translate(
718                                                 blockpos_nodes.Z + z,
719                                                 blockpos_nodes.X + x);
720                                 tcoord_translate.rotateBy(tcoord_angle);
721                                 tcoord_translate.X -= floor(tcoord_translate.X);
722                                 tcoord_translate.Y -= floor(tcoord_translate.Y);
723
724                                 for(s32 i=0; i<4; i++)
725                                 {
726                                         vertices[i].TCoords.rotateBy(
727                                                         tcoord_angle,
728                                                         tcoord_center);
729                                         vertices[i].TCoords += tcoord_translate;
730                                 }
731
732                                 v2f t = vertices[0].TCoords;
733                                 vertices[0].TCoords = vertices[2].TCoords;
734                                 vertices[2].TCoords = t;
735
736                                 u16 indices[] = {0,1,2,2,3,0};
737                                 // Add to mesh collector
738                                 collector.append(tile_liquid, vertices, 4, indices, 6);
739                         }
740                 break;}
741                 case NDT_GLASSLIKE:
742                 {
743                         TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data);
744
745                         u16 l = getInteriorLight(n, 1, nodedef);
746                         video::SColor c = MapBlock_LightColor(255, l, f.light_source);
747
748                         for(u32 j=0; j<6; j++)
749                         {
750                                 // Check this neighbor
751                                 v3s16 n2p = blockpos_nodes + p + g_6dirs[j];
752                                 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
753                                 // Don't make face if neighbor is of same type
754                                 if(n2.getContent() == n.getContent())
755                                         continue;
756
757                                 // The face at Z+
758                                 video::S3DVertex vertices[4] = {
759                                         video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
760                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
761                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 0,0),
762                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
763                                 };
764                                 
765                                 // Rotations in the g_6dirs format
766                                 if(j == 0) // Z+
767                                         for(u16 i=0; i<4; i++)
768                                                 vertices[i].Pos.rotateXZBy(0);
769                                 else if(j == 1) // Y+
770                                         for(u16 i=0; i<4; i++)
771                                                 vertices[i].Pos.rotateYZBy(-90);
772                                 else if(j == 2) // X+
773                                         for(u16 i=0; i<4; i++)
774                                                 vertices[i].Pos.rotateXZBy(-90);
775                                 else if(j == 3) // Z-
776                                         for(u16 i=0; i<4; i++)
777                                                 vertices[i].Pos.rotateXZBy(180);
778                                 else if(j == 4) // Y-
779                                         for(u16 i=0; i<4; i++)
780                                                 vertices[i].Pos.rotateYZBy(90);
781                                 else if(j == 5) // X-
782                                         for(u16 i=0; i<4; i++)
783                                                 vertices[i].Pos.rotateXZBy(90);
784
785                                 for(u16 i=0; i<4; i++){
786                                         vertices[i].Pos += intToFloat(p, BS);
787                                 }
788
789                                 u16 indices[] = {0,1,2,2,3,0};
790                                 // Add to mesh collector
791                                 collector.append(tile, vertices, 4, indices, 6);
792                         }
793                 break;}
794                 case NDT_GLASSLIKE_FRAMED:
795                 {
796                         static const v3s16 dirs[6] = {
797                                 v3s16( 0, 1, 0),
798                                 v3s16( 0,-1, 0),
799                                 v3s16( 1, 0, 0),
800                                 v3s16(-1, 0, 0),
801                                 v3s16( 0, 0, 1),
802                                 v3s16( 0, 0,-1)
803                         };
804
805                         u8 i;
806                         TileSpec tiles[6];
807                         for (i = 0; i < 6; i++)
808                                 tiles[i] = getNodeTile(n, p, dirs[i], data);
809                         
810                         TileSpec glass_tiles[6];
811                         if (tiles[1].texture && tiles[2].texture && tiles[3].texture) {
812                                 glass_tiles[0] = tiles[2];
813                                 glass_tiles[1] = tiles[3];
814                                 glass_tiles[2] = tiles[1];
815                                 glass_tiles[3] = tiles[1];
816                                 glass_tiles[4] = tiles[1];
817                                 glass_tiles[5] = tiles[1];
818                         } else {
819                                 for (i = 0; i < 6; i++)
820                                         glass_tiles[i] = tiles[1];      
821                         }
822                         
823                         u8 param2 = n.getParam2();
824                         bool H_merge = ! bool(param2 & 128);
825                         bool V_merge = ! bool(param2 & 64);
826                         param2  = param2 & 63;
827                         
828                         u16 l = getInteriorLight(n, 1, nodedef);
829                         video::SColor c = MapBlock_LightColor(255, l, f.light_source);
830                         v3f pos = intToFloat(p, BS);
831                         static const float a = BS / 2;
832                         static const float g = a - 0.003;
833                         static const float b = .876 * ( BS / 2 );
834                         
835                         static const aabb3f frame_edges[12] = {
836                                 aabb3f( b, b,-a, a, a, a), // y+
837                                 aabb3f(-a, b,-a,-b, a, a), // y+
838                                 aabb3f( b,-a,-a, a,-b, a), // y-
839                                 aabb3f(-a,-a,-a,-b,-b, a), // y-
840                                 aabb3f( b,-a, b, a, a, a), // x+
841                                 aabb3f( b,-a,-a, a, a,-b), // x+
842                                 aabb3f(-a,-a, b,-b, a, a), // x-
843                                 aabb3f(-a,-a,-a,-b, a,-b), // x-
844                                 aabb3f(-a, b, b, a, a, a), // z+
845                                 aabb3f(-a,-a, b, a,-b, a), // z+
846                                 aabb3f(-a,-a,-a, a,-b,-b), // z-
847                                 aabb3f(-a, b,-a, a, a,-b)  // z-
848                         };
849                         static const aabb3f glass_faces[6] = {
850                                 aabb3f(-g, g,-g, g, g, g), // y+
851                                 aabb3f(-g,-g,-g, g,-g, g), // y-
852                                 aabb3f( g,-g,-g, g, g, g), // x+
853                                 aabb3f(-g,-g,-g,-g, g, g), // x-
854                                 aabb3f(-g,-g, g, g, g, g), // z+
855                                 aabb3f(-g,-g,-g, g, g,-g)  // z-
856                         };
857                         
858                         // table of node visible faces, 0 = invisible
859                         int visible_faces[6] = {0,0,0,0,0,0};
860                         
861                         // table of neighbours, 1 = same type, checked with g_26dirs
862                         int nb[18] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
863                         
864                         // g_26dirs to check when only horizontal merge is allowed
865                         int nb_H_dirs[8] = {0,2,3,5,10,11,12,13};
866                         
867                         content_t current = n.getContent();
868                         content_t n2c;
869                         MapNode n2;
870                         v3s16 n2p;
871
872                         // neighbours checks for frames visibility
873
874                         if (!H_merge && V_merge) {
875                                 n2p = blockpos_nodes + p + g_26dirs[1];
876                                 n2 = data->m_vmanip.getNodeNoEx(n2p);
877                                 n2c = n2.getContent();
878                                 if (n2c == current || n2c == CONTENT_IGNORE)
879                                         nb[1] = 1;
880                                 n2p = blockpos_nodes + p + g_26dirs[4];
881                                 n2 = data->m_vmanip.getNodeNoEx(n2p);
882                                 n2c = n2.getContent();
883                                 if (n2c == current || n2c == CONTENT_IGNORE)
884                                         nb[4] = 1;      
885                         } else if (H_merge && !V_merge) {
886                                 for(i = 0; i < 8; i++) {
887                                         n2p = blockpos_nodes + p + g_26dirs[nb_H_dirs[i]];
888                                         n2 = data->m_vmanip.getNodeNoEx(n2p);
889                                         n2c = n2.getContent();
890                                         if (n2c == current || n2c == CONTENT_IGNORE)
891                                                 nb[nb_H_dirs[i]] = 1;           
892                                 }
893                         } else if (H_merge && V_merge) {
894                                 for(i = 0; i < 18; i++) {
895                                         n2p = blockpos_nodes + p + g_26dirs[i];
896                                         n2 = data->m_vmanip.getNodeNoEx(n2p);
897                                         n2c = n2.getContent();
898                                         if (n2c == current || n2c == CONTENT_IGNORE)
899                                                 nb[i] = 1;
900                                 }
901                         }
902
903                         // faces visibility checks
904
905                         if (!V_merge) {
906                                 visible_faces[0] = 1;
907                                 visible_faces[1] = 1;
908                         } else {
909                                 for(i = 0; i < 2; i++) {
910                                         n2p = blockpos_nodes + p + dirs[i];
911                                         n2 = data->m_vmanip.getNodeNoEx(n2p);
912                                         n2c = n2.getContent();
913                                         if (n2c != current)
914                                                 visible_faces[i] = 1;
915                                 }
916                         }
917                                 
918                         if (!H_merge) {
919                                 visible_faces[2] = 1;
920                                 visible_faces[3] = 1;
921                                 visible_faces[4] = 1;
922                                 visible_faces[5] = 1;
923                         } else {
924                                 for(i = 2; i < 6; i++) {
925                                         n2p = blockpos_nodes + p + dirs[i];
926                                         n2 = data->m_vmanip.getNodeNoEx(n2p);
927                                         n2c = n2.getContent();
928                                         if (n2c != current)
929                                                 visible_faces[i] = 1;
930                                 }
931                         }
932         
933                         static const u8 nb_triplet[12*3] = {
934                                 1,2, 7,  1,5, 6,  4,2,15,  4,5,14,
935                                 2,0,11,  2,3,13,  5,0,10,  5,3,12,
936                                 0,1, 8,  0,4,16,  3,4,17,  3,1, 9
937                         };
938
939                         f32 tx1, ty1, tz1, tx2, ty2, tz2;
940                         aabb3f box;
941
942                         for(i = 0; i < 12; i++)
943                         {
944                                 int edge_invisible;
945                                 if (nb[nb_triplet[i*3+2]])
946                                         edge_invisible = nb[nb_triplet[i*3]] & nb[nb_triplet[i*3+1]];
947                                 else
948                                         edge_invisible = nb[nb_triplet[i*3]] ^ nb[nb_triplet[i*3+1]];
949                                 if (edge_invisible)
950                                         continue;
951                                 box = frame_edges[i];
952                                 box.MinEdge += pos;
953                                 box.MaxEdge += pos;
954                                 tx1 = (box.MinEdge.X / BS) + 0.5;
955                                 ty1 = (box.MinEdge.Y / BS) + 0.5;
956                                 tz1 = (box.MinEdge.Z / BS) + 0.5;
957                                 tx2 = (box.MaxEdge.X / BS) + 0.5;
958                                 ty2 = (box.MaxEdge.Y / BS) + 0.5;
959                                 tz2 = (box.MaxEdge.Z / BS) + 0.5;
960                                 f32 txc1[24] = {
961                                         tx1,   1-tz2,   tx2, 1-tz1,
962                                         tx1,     tz1,   tx2,   tz2,
963                                         tz1,   1-ty2,   tz2, 1-ty1,
964                                         1-tz2, 1-ty2, 1-tz1, 1-ty1,
965                                         1-tx2, 1-ty2, 1-tx1, 1-ty1,
966                                         tx1,   1-ty2,   tx2, 1-ty1,
967                                 };
968                                 makeCuboid(&collector, box, &tiles[0], 1, c, txc1);
969                         }
970
971                         for(i = 0; i < 6; i++)
972                         {
973                                 if (!visible_faces[i])
974                                         continue;
975                                 box = glass_faces[i];
976                                 box.MinEdge += pos;
977                                 box.MaxEdge += pos;
978                                 tx1 = (box.MinEdge.X / BS) + 0.5;
979                                 ty1 = (box.MinEdge.Y / BS) + 0.5;
980                                 tz1 = (box.MinEdge.Z / BS) + 0.5;
981                                 tx2 = (box.MaxEdge.X / BS) + 0.5;
982                                 ty2 = (box.MaxEdge.Y / BS) + 0.5;
983                                 tz2 = (box.MaxEdge.Z / BS) + 0.5;
984                                 f32 txc2[24] = {
985                                         tx1,   1-tz2,   tx2, 1-tz1,
986                                         tx1,     tz1,   tx2,   tz2,
987                                         tz1,   1-ty2,   tz2, 1-ty1,
988                                         1-tz2, 1-ty2, 1-tz1, 1-ty1,
989                                         1-tx2, 1-ty2, 1-tx1, 1-ty1,
990                                         tx1,   1-ty2,   tx2, 1-ty1,
991                                 };
992                                 makeCuboid(&collector, box, &glass_tiles[i], 1, c, txc2);
993                         }
994
995                         if (param2 > 0 && f.special_tiles[0].texture) {
996                                 // Interior volume level is in range 0 .. 63,
997                                 // convert it to -0.5 .. 0.5
998                                 float vlev = (((float)param2 / 63.0 ) * 2.0 - 1.0);
999                                 TileSpec interior_tiles[6];
1000                                 for (i = 0; i < 6; i++)
1001                                         interior_tiles[i] = f.special_tiles[0];
1002                                 float offset = 0.003;
1003                                 box = aabb3f(visible_faces[3] ? -b : -a + offset,
1004                                                          visible_faces[1] ? -b : -a + offset,
1005                                                          visible_faces[5] ? -b : -a + offset,
1006                                                          visible_faces[2] ? b : a - offset,
1007                                                          visible_faces[0] ? b * vlev : a * vlev - offset,
1008                                                          visible_faces[4] ? b : a - offset);
1009                                 box.MinEdge += pos;
1010                                 box.MaxEdge += pos;
1011                                 tx1 = (box.MinEdge.X / BS) + 0.5;
1012                                 ty1 = (box.MinEdge.Y / BS) + 0.5;
1013                                 tz1 = (box.MinEdge.Z / BS) + 0.5;
1014                                 tx2 = (box.MaxEdge.X / BS) + 0.5;
1015                                 ty2 = (box.MaxEdge.Y / BS) + 0.5;
1016                                 tz2 = (box.MaxEdge.Z / BS) + 0.5;
1017                                 f32 txc3[24] = {
1018                                         tx1,   1-tz2,   tx2, 1-tz1,
1019                                         tx1,     tz1,   tx2,   tz2,
1020                                         tz1,   1-ty2,   tz2, 1-ty1,
1021                                         1-tz2, 1-ty2, 1-tz1, 1-ty1,
1022                                         1-tx2, 1-ty2, 1-tx1, 1-ty1,
1023                                         tx1,   1-ty2,   tx2, 1-ty1,
1024                                 };
1025                                 makeCuboid(&collector, box, interior_tiles, 6, c,  txc3);
1026                         }
1027                 break;}
1028                 case NDT_ALLFACES:
1029                 {
1030                         TileSpec tile_leaves = getNodeTile(n, p,
1031                                         v3s16(0,0,0), data);
1032
1033                         u16 l = getInteriorLight(n, 1, nodedef);
1034                         video::SColor c = MapBlock_LightColor(255, l, f.light_source);
1035
1036                         v3f pos = intToFloat(p, BS);
1037                         aabb3f box(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2);
1038                         box.MinEdge += pos;
1039                         box.MaxEdge += pos;
1040                         makeCuboid(&collector, box, &tile_leaves, 1, c, NULL);
1041                 break;}
1042                 case NDT_ALLFACES_OPTIONAL:
1043                         // This is always pre-converted to something else
1044                         assert(0);
1045                         break;
1046                 case NDT_TORCHLIKE:
1047                 {
1048                         v3s16 dir = n.getWallMountedDir(nodedef);
1049                         
1050                         u8 tileindex = 0;
1051                         if(dir == v3s16(0,-1,0)){
1052                                 tileindex = 0; // floor
1053                         } else if(dir == v3s16(0,1,0)){
1054                                 tileindex = 1; // ceiling
1055                         // For backwards compatibility
1056                         } else if(dir == v3s16(0,0,0)){
1057                                 tileindex = 0; // floor
1058                         } else {
1059                                 tileindex = 2; // side
1060                         }
1061
1062                         TileSpec tile = getNodeTileN(n, p, tileindex, data);
1063                         tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
1064                         tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1065
1066                         u16 l = getInteriorLight(n, 1, nodedef);
1067                         video::SColor c = MapBlock_LightColor(255, l, f.light_source);
1068
1069                         float s = BS/2*f.visual_scale;
1070                         // Wall at X+ of node
1071                         video::S3DVertex vertices[4] =
1072                         {
1073                                 video::S3DVertex(-s,-s,0, 0,0,0, c, 0,1),
1074                                 video::S3DVertex( s,-s,0, 0,0,0, c, 1,1),
1075                                 video::S3DVertex( s, s,0, 0,0,0, c, 1,0),
1076                                 video::S3DVertex(-s, s,0, 0,0,0, c, 0,0),
1077                         };
1078
1079                         for(s32 i=0; i<4; i++)
1080                         {
1081                                 if(dir == v3s16(1,0,0))
1082                                         vertices[i].Pos.rotateXZBy(0);
1083                                 if(dir == v3s16(-1,0,0))
1084                                         vertices[i].Pos.rotateXZBy(180);
1085                                 if(dir == v3s16(0,0,1))
1086                                         vertices[i].Pos.rotateXZBy(90);
1087                                 if(dir == v3s16(0,0,-1))
1088                                         vertices[i].Pos.rotateXZBy(-90);
1089                                 if(dir == v3s16(0,-1,0))
1090                                         vertices[i].Pos.rotateXZBy(45);
1091                                 if(dir == v3s16(0,1,0))
1092                                         vertices[i].Pos.rotateXZBy(-45);
1093
1094                                 vertices[i].Pos += intToFloat(p, BS);
1095                         }
1096
1097                         u16 indices[] = {0,1,2,2,3,0};
1098                         // Add to mesh collector
1099                         collector.append(tile, vertices, 4, indices, 6);
1100                 break;}
1101                 case NDT_SIGNLIKE:
1102                 {
1103                         TileSpec tile = getNodeTileN(n, p, 0, data);
1104                         tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
1105                         tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1106
1107                         u16 l = getInteriorLight(n, 0, nodedef);
1108                         video::SColor c = MapBlock_LightColor(255, l, f.light_source);
1109                                 
1110                         float d = (float)BS/16;
1111                         float s = BS/2*f.visual_scale;
1112                         // Wall at X+ of node
1113                         video::S3DVertex vertices[4] =
1114                         {
1115                                 video::S3DVertex(BS/2-d,  s,  s, 0,0,0, c, 0,0),
1116                                 video::S3DVertex(BS/2-d,  s, -s, 0,0,0, c, 1,0),
1117                                 video::S3DVertex(BS/2-d, -s, -s, 0,0,0, c, 1,1),
1118                                 video::S3DVertex(BS/2-d, -s,  s, 0,0,0, c, 0,1),
1119                         };
1120
1121                         v3s16 dir = n.getWallMountedDir(nodedef);
1122
1123                         for(s32 i=0; i<4; i++)
1124                         {
1125                                 if(dir == v3s16(1,0,0))
1126                                         vertices[i].Pos.rotateXZBy(0);
1127                                 if(dir == v3s16(-1,0,0))
1128                                         vertices[i].Pos.rotateXZBy(180);
1129                                 if(dir == v3s16(0,0,1))
1130                                         vertices[i].Pos.rotateXZBy(90);
1131                                 if(dir == v3s16(0,0,-1))
1132                                         vertices[i].Pos.rotateXZBy(-90);
1133                                 if(dir == v3s16(0,-1,0))
1134                                         vertices[i].Pos.rotateXYBy(-90);
1135                                 if(dir == v3s16(0,1,0))
1136                                         vertices[i].Pos.rotateXYBy(90);
1137
1138                                 vertices[i].Pos += intToFloat(p, BS);
1139                         }
1140
1141                         u16 indices[] = {0,1,2,2,3,0};
1142                         // Add to mesh collector
1143                         collector.append(tile, vertices, 4, indices, 6);
1144                 break;}
1145                 case NDT_PLANTLIKE:
1146                 {
1147                         TileSpec tile = getNodeTileN(n, p, 0, data);
1148                         tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1149                         
1150                         u16 l = getInteriorLight(n, 1, nodedef);
1151                         video::SColor c = MapBlock_LightColor(255, l, f.light_source);
1152
1153                         float s = BS/2*f.visual_scale;
1154
1155                         for(u32 j=0; j<2; j++)
1156                         {
1157                                 video::S3DVertex vertices[4] =
1158                                 {
1159                                         video::S3DVertex(-s,-BS/2,      0, 0,0,0, c, 0,1),
1160                                         video::S3DVertex( s,-BS/2,      0, 0,0,0, c, 1,1),
1161                                         video::S3DVertex( s,-BS/2 + s*2,0, 0,0,0, c, 1,0),
1162                                         video::S3DVertex(-s,-BS/2 + s*2,0, 0,0,0, c, 0,0),
1163                                 };
1164
1165                                 if(j == 0)
1166                                 {
1167                                         for(u16 i=0; i<4; i++)
1168                                                 vertices[i].Pos.rotateXZBy(46 + n.param2 * 2);
1169                                 }
1170                                 else if(j == 1)
1171                                 {
1172                                         for(u16 i=0; i<4; i++)
1173                                                 vertices[i].Pos.rotateXZBy(-44 + n.param2 * 2);
1174                                 }
1175
1176                                 for(u16 i=0; i<4; i++)
1177                                 {
1178                                         vertices[i].Pos *= f.visual_scale;
1179                                         vertices[i].Pos += intToFloat(p, BS);
1180                                 }
1181
1182                                 u16 indices[] = {0,1,2,2,3,0};
1183                                 // Add to mesh collector
1184                                 collector.append(tile, vertices, 4, indices, 6);
1185                         }
1186                 break;}
1187                 case NDT_FENCELIKE:
1188                 {
1189                         TileSpec tile = getNodeTile(n, p, v3s16(0,0,0), data);
1190                         TileSpec tile_nocrack = tile;
1191                         tile_nocrack.material_flags &= ~MATERIAL_FLAG_CRACK;
1192
1193                         // Put wood the right way around in the posts
1194                         TileSpec tile_rot = tile;
1195                         tile_rot.rotation = 1;
1196
1197                         u16 l = getInteriorLight(n, 1, nodedef);
1198                         video::SColor c = MapBlock_LightColor(255, l, f.light_source);
1199
1200                         const f32 post_rad=(f32)BS/8;
1201                         const f32 bar_rad=(f32)BS/16;
1202                         const f32 bar_len=(f32)(BS/2)-post_rad;
1203
1204                         v3f pos = intToFloat(p, BS);
1205
1206                         // The post - always present
1207                         aabb3f post(-post_rad,-BS/2,-post_rad,post_rad,BS/2,post_rad);
1208                         post.MinEdge += pos;
1209                         post.MaxEdge += pos;
1210                         f32 postuv[24]={
1211                                         6/16.,6/16.,10/16.,10/16.,
1212                                         6/16.,6/16.,10/16.,10/16.,
1213                                         0/16.,0,4/16.,1,
1214                                         4/16.,0,8/16.,1,
1215                                         8/16.,0,12/16.,1,
1216                                         12/16.,0,16/16.,1};
1217                         makeCuboid(&collector, post, &tile_rot, 1, c, postuv);
1218
1219                         // Now a section of fence, +X, if there's a post there
1220                         v3s16 p2 = p;
1221                         p2.X++;
1222                         MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1223                         const ContentFeatures *f2 = &nodedef->get(n2);
1224                         if(f2->drawtype == NDT_FENCELIKE)
1225                         {
1226                                 aabb3f bar(-bar_len+BS/2,-bar_rad+BS/4,-bar_rad,
1227                                                 bar_len+BS/2,bar_rad+BS/4,bar_rad);
1228                                 bar.MinEdge += pos;
1229                                 bar.MaxEdge += pos;
1230                                 f32 xrailuv[24]={
1231                                         0/16.,2/16.,16/16.,4/16.,
1232                                         0/16.,4/16.,16/16.,6/16.,
1233                                         6/16.,6/16.,8/16.,8/16.,
1234                                         10/16.,10/16.,12/16.,12/16.,
1235                                         0/16.,8/16.,16/16.,10/16.,
1236                                         0/16.,14/16.,16/16.,16/16.};
1237                                 makeCuboid(&collector, bar, &tile_nocrack, 1,
1238                                                 c, xrailuv);
1239                                 bar.MinEdge.Y -= BS/2;
1240                                 bar.MaxEdge.Y -= BS/2;
1241                                 makeCuboid(&collector, bar, &tile_nocrack, 1,
1242                                                 c, xrailuv);
1243                         }
1244
1245                         // Now a section of fence, +Z, if there's a post there
1246                         p2 = p;
1247                         p2.Z++;
1248                         n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1249                         f2 = &nodedef->get(n2);
1250                         if(f2->drawtype == NDT_FENCELIKE)
1251                         {
1252                                 aabb3f bar(-bar_rad,-bar_rad+BS/4,-bar_len+BS/2,
1253                                                 bar_rad,bar_rad+BS/4,bar_len+BS/2);
1254                                 bar.MinEdge += pos;
1255                                 bar.MaxEdge += pos;
1256                                 f32 zrailuv[24]={
1257                                         3/16.,1/16.,5/16.,5/16., // cannot rotate; stretch
1258                                         4/16.,1/16.,6/16.,5/16., // for wood texture instead
1259                                         0/16.,9/16.,16/16.,11/16.,
1260                                         0/16.,6/16.,16/16.,8/16.,
1261                                         6/16.,6/16.,8/16.,8/16.,
1262                                         10/16.,10/16.,12/16.,12/16.};
1263                                 makeCuboid(&collector, bar, &tile_nocrack, 1,
1264                                                 c, zrailuv);
1265                                 bar.MinEdge.Y -= BS/2;
1266                                 bar.MaxEdge.Y -= BS/2;
1267                                 makeCuboid(&collector, bar, &tile_nocrack, 1,
1268                                                 c, zrailuv);
1269                         }
1270                 break;}
1271                 case NDT_RAILLIKE:
1272                 {
1273                         bool is_rail_x [] = { false, false };  /* x-1, x+1 */
1274                         bool is_rail_z [] = { false, false };  /* z-1, z+1 */
1275
1276                         bool is_rail_z_minus_y [] = { false, false };  /* z-1, z+1; y-1 */
1277                         bool is_rail_x_minus_y [] = { false, false };  /* x-1, z+1; y-1 */
1278                         bool is_rail_z_plus_y [] = { false, false };  /* z-1, z+1; y+1 */
1279                         bool is_rail_x_plus_y [] = { false, false };  /* x-1, x+1; y+1 */
1280
1281                         MapNode n_minus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1,y,z));
1282                         MapNode n_plus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1,y,z));
1283                         MapNode n_minus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z-1));
1284                         MapNode n_plus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z+1));
1285                         MapNode n_plus_x_plus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1, y+1, z));
1286                         MapNode n_plus_x_minus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1, y-1, z));
1287                         MapNode n_minus_x_plus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1, y+1, z));
1288                         MapNode n_minus_x_minus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1, y-1, z));
1289                         MapNode n_plus_z_plus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y+1, z+1));
1290                         MapNode n_minus_z_plus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y+1, z-1));
1291                         MapNode n_plus_z_minus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y-1, z+1));
1292                         MapNode n_minus_z_minus_y = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x, y-1, z-1));
1293
1294                         content_t thiscontent = n.getContent();
1295                         std::string groupname = "connect_to_raillike"; // name of the group that enables connecting to raillike nodes of different kind
1296                         bool self_connect_to_raillike = ((ItemGroupList) nodedef->get(n).groups)[groupname] != 0;
1297
1298                         if ((nodedef->get(n_minus_x).drawtype == NDT_RAILLIKE
1299                                         && ((ItemGroupList) nodedef->get(n_minus_x).groups)[groupname] != 0
1300                                         && self_connect_to_raillike)
1301                                         || n_minus_x.getContent() == thiscontent)
1302                                 is_rail_x[0] = true;
1303
1304                         if ((nodedef->get(n_minus_x_minus_y).drawtype == NDT_RAILLIKE
1305                                         && ((ItemGroupList) nodedef->get(n_minus_x_minus_y).groups)[groupname] != 0
1306                                         && self_connect_to_raillike)
1307                                         || n_minus_x_minus_y.getContent() == thiscontent)
1308                                 is_rail_x_minus_y[0] = true;
1309
1310                         if ((nodedef->get(n_minus_x_plus_y).drawtype == NDT_RAILLIKE
1311                                         && ((ItemGroupList) nodedef->get(n_minus_x_plus_y).groups)[groupname] != 0
1312                                         && self_connect_to_raillike)
1313                                         || n_minus_x_plus_y.getContent() == thiscontent)
1314                                 is_rail_x_plus_y[0] = true;
1315
1316                         if ((nodedef->get(n_plus_x).drawtype == NDT_RAILLIKE
1317                                         && ((ItemGroupList) nodedef->get(n_plus_x).groups)[groupname] != 0
1318                                         && self_connect_to_raillike)
1319                                         || n_plus_x.getContent() == thiscontent)
1320                                 is_rail_x[1] = true;
1321
1322                         if ((nodedef->get(n_plus_x_minus_y).drawtype == NDT_RAILLIKE
1323                                         && ((ItemGroupList) nodedef->get(n_plus_x_minus_y).groups)[groupname] != 0
1324                                         && self_connect_to_raillike)
1325                                         || n_plus_x_minus_y.getContent() == thiscontent)
1326                                 is_rail_x_minus_y[1] = true;
1327
1328                         if ((nodedef->get(n_plus_x_plus_y).drawtype == NDT_RAILLIKE
1329                                         && ((ItemGroupList) nodedef->get(n_plus_x_plus_y).groups)[groupname] != 0
1330                                         && self_connect_to_raillike)
1331                                         || n_plus_x_plus_y.getContent() == thiscontent)
1332                                 is_rail_x_plus_y[1] = true;
1333
1334                         if ((nodedef->get(n_minus_z).drawtype == NDT_RAILLIKE
1335                                         && ((ItemGroupList) nodedef->get(n_minus_z).groups)[groupname] != 0
1336                                         && self_connect_to_raillike)
1337                                         || n_minus_z.getContent() == thiscontent)
1338                                 is_rail_z[0] = true;
1339
1340                         if ((nodedef->get(n_minus_z_minus_y).drawtype == NDT_RAILLIKE
1341                                         && ((ItemGroupList) nodedef->get(n_minus_z_minus_y).groups)[groupname] != 0
1342                                         && self_connect_to_raillike)
1343                                         || n_minus_z_minus_y.getContent() == thiscontent)
1344                                 is_rail_z_minus_y[0] = true;
1345
1346                         if ((nodedef->get(n_minus_z_plus_y).drawtype == NDT_RAILLIKE
1347                                         && ((ItemGroupList) nodedef->get(n_minus_z_plus_y).groups)[groupname] != 0
1348                                         && self_connect_to_raillike)
1349                                         || n_minus_z_plus_y.getContent() == thiscontent)
1350                                 is_rail_z_plus_y[0] = true;
1351
1352                         if ((nodedef->get(n_plus_z).drawtype == NDT_RAILLIKE
1353                                         && ((ItemGroupList) nodedef->get(n_plus_z).groups)[groupname] != 0
1354                                         && self_connect_to_raillike)
1355                                         || n_plus_z.getContent() == thiscontent)
1356                                 is_rail_z[1] = true;
1357
1358                         if ((nodedef->get(n_plus_z_minus_y).drawtype == NDT_RAILLIKE
1359                                         && ((ItemGroupList) nodedef->get(n_plus_z_minus_y).groups)[groupname] != 0
1360                                         && self_connect_to_raillike)
1361                                         || n_plus_z_minus_y.getContent() == thiscontent)
1362                                 is_rail_z_minus_y[1] = true;
1363
1364                         if ((nodedef->get(n_plus_z_plus_y).drawtype == NDT_RAILLIKE
1365                                         && ((ItemGroupList) nodedef->get(n_plus_z_plus_y).groups)[groupname] != 0
1366                                         && self_connect_to_raillike)
1367                                         || n_plus_z_plus_y.getContent() == thiscontent)
1368                                 is_rail_z_plus_y[1] = true;
1369
1370                         bool is_rail_x_all[] = {false, false};
1371                         bool is_rail_z_all[] = {false, false};
1372                         is_rail_x_all[0]=is_rail_x[0] || is_rail_x_minus_y[0] || is_rail_x_plus_y[0];
1373                         is_rail_x_all[1]=is_rail_x[1] || is_rail_x_minus_y[1] || is_rail_x_plus_y[1];
1374                         is_rail_z_all[0]=is_rail_z[0] || is_rail_z_minus_y[0] || is_rail_z_plus_y[0];
1375                         is_rail_z_all[1]=is_rail_z[1] || is_rail_z_minus_y[1] || is_rail_z_plus_y[1];
1376
1377                         // reasonable default, flat straight unrotated rail
1378                         bool is_straight = true;
1379                         int adjacencies = 0;
1380                         int angle = 0;
1381                         u8 tileindex = 0;
1382
1383                         // check for sloped rail
1384                         if (is_rail_x_plus_y[0] || is_rail_x_plus_y[1] || is_rail_z_plus_y[0] || is_rail_z_plus_y[1])
1385                         {
1386                                 adjacencies = 5; //5 means sloped
1387                                 is_straight = true; // sloped is always straight
1388                         }
1389                         else
1390                         {
1391                                 // is really straight, rails on both sides
1392                                 is_straight = (is_rail_x_all[0] && is_rail_x_all[1]) || (is_rail_z_all[0] && is_rail_z_all[1]);
1393                                 adjacencies = is_rail_x_all[0] + is_rail_x_all[1] + is_rail_z_all[0] + is_rail_z_all[1];
1394                         }
1395
1396                         switch (adjacencies) {
1397                         case 1:
1398                                 if(is_rail_x_all[0] || is_rail_x_all[1])
1399                                         angle = 90;
1400                                 break;
1401                         case 2:
1402                                 if(!is_straight)
1403                                         tileindex = 1; // curved
1404                                 if(is_rail_x_all[0] && is_rail_x_all[1])
1405                                         angle = 90;
1406                                 if(is_rail_z_all[0] && is_rail_z_all[1]){
1407                                         if (is_rail_z_plus_y[0])
1408                                                 angle = 180;
1409                                 }
1410                                 else if(is_rail_x_all[0] && is_rail_z_all[0])
1411                                         angle = 270;
1412                                 else if(is_rail_x_all[0] && is_rail_z_all[1])
1413                                         angle = 180;
1414                                 else if(is_rail_x_all[1] && is_rail_z_all[1])
1415                                         angle = 90;
1416                                 break;
1417                         case 3:
1418                                 // here is where the potential to 'switch' a junction is, but not implemented at present
1419                                 tileindex = 2; // t-junction
1420                                 if(!is_rail_x_all[1])
1421                                         angle=180;
1422                                 if(!is_rail_z_all[0])
1423                                         angle=90;
1424                                 if(!is_rail_z_all[1])
1425                                         angle=270;
1426                                 break;
1427                         case 4:
1428                                 tileindex = 3; // crossing
1429                                 break;
1430                         case 5: //sloped
1431                                 if(is_rail_z_plus_y[0])
1432                                         angle = 180;
1433                                 if(is_rail_x_plus_y[0])
1434                                         angle = 90;
1435                                 if(is_rail_x_plus_y[1])
1436                                         angle = -90;
1437                                 break;
1438                         default:
1439                                 break;
1440                         }
1441
1442                         TileSpec tile = getNodeTileN(n, p, tileindex, data);
1443                         tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
1444                         tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
1445
1446                         u16 l = getInteriorLight(n, 0, nodedef);
1447                         video::SColor c = MapBlock_LightColor(255, l, f.light_source);
1448
1449                         float d = (float)BS/64;
1450                         
1451                         char g=-1;
1452                         if (is_rail_x_plus_y[0] || is_rail_x_plus_y[1] || is_rail_z_plus_y[0] || is_rail_z_plus_y[1])
1453                                 g=1; //Object is at a slope
1454
1455                         video::S3DVertex vertices[4] =
1456                         {
1457                                         video::S3DVertex(-BS/2,-BS/2+d,-BS/2, 0,0,0, c, 0,1),
1458                                         video::S3DVertex(BS/2,-BS/2+d,-BS/2, 0,0,0, c, 1,1),
1459                                         video::S3DVertex(BS/2,g*BS/2+d,BS/2, 0,0,0, c, 1,0),
1460                                         video::S3DVertex(-BS/2,g*BS/2+d,BS/2, 0,0,0, c, 0,0),
1461                         };
1462
1463                         for(s32 i=0; i<4; i++)
1464                         {
1465                                 if(angle != 0)
1466                                         vertices[i].Pos.rotateXZBy(angle);
1467                                 vertices[i].Pos += intToFloat(p, BS);
1468                         }
1469
1470                         u16 indices[] = {0,1,2,2,3,0};
1471                         collector.append(tile, vertices, 4, indices, 6);
1472                 break;}
1473                 case NDT_NODEBOX:
1474                 {
1475                         static const v3s16 tile_dirs[6] = {
1476                                 v3s16(0, 1, 0),
1477                                 v3s16(0, -1, 0),
1478                                 v3s16(1, 0, 0),
1479                                 v3s16(-1, 0, 0),
1480                                 v3s16(0, 0, 1),
1481                                 v3s16(0, 0, -1)
1482                         };
1483                         TileSpec tiles[6];
1484                         
1485                         u16 l = getInteriorLight(n, 1, nodedef);
1486                         video::SColor c = MapBlock_LightColor(255, l, f.light_source);
1487
1488                         v3f pos = intToFloat(p, BS);
1489
1490                         std::vector<aabb3f> boxes = n.getNodeBoxes(nodedef);
1491                         for(std::vector<aabb3f>::iterator
1492                                         i = boxes.begin();
1493                                         i != boxes.end(); i++)
1494                         {
1495                         for(int j = 0; j < 6; j++)
1496                                 {
1497                                 // Handles facedir rotation for textures
1498                                 tiles[j] = getNodeTile(n, p, tile_dirs[j], data);
1499                                 }
1500                                 aabb3f box = *i;
1501                                 box.MinEdge += pos;
1502                                 box.MaxEdge += pos;
1503                                 
1504                                 f32 temp;
1505                                 if (box.MinEdge.X > box.MaxEdge.X)
1506                                 {
1507                                         temp=box.MinEdge.X;
1508                                         box.MinEdge.X=box.MaxEdge.X;
1509                                         box.MaxEdge.X=temp;
1510                                 }
1511                                 if (box.MinEdge.Y > box.MaxEdge.Y)
1512                                 {
1513                                         temp=box.MinEdge.Y;
1514                                         box.MinEdge.Y=box.MaxEdge.Y;
1515                                         box.MaxEdge.Y=temp;
1516                                 }
1517                                 if (box.MinEdge.Z > box.MaxEdge.Z)
1518                                 {
1519                                         temp=box.MinEdge.Z;
1520                                         box.MinEdge.Z=box.MaxEdge.Z;
1521                                         box.MaxEdge.Z=temp;
1522                                 }
1523
1524                                 //
1525                                 // Compute texture coords
1526                                 f32 tx1 = (box.MinEdge.X/BS)+0.5;
1527                                 f32 ty1 = (box.MinEdge.Y/BS)+0.5;
1528                                 f32 tz1 = (box.MinEdge.Z/BS)+0.5;
1529                                 f32 tx2 = (box.MaxEdge.X/BS)+0.5;
1530                                 f32 ty2 = (box.MaxEdge.Y/BS)+0.5;
1531                                 f32 tz2 = (box.MaxEdge.Z/BS)+0.5;
1532                                 f32 txc[24] = {
1533                                         // up
1534                                         tx1, 1-tz2, tx2, 1-tz1,
1535                                         // down
1536                                         tx1, tz1, tx2, tz2,
1537                                         // right
1538                                         tz1, 1-ty2, tz2, 1-ty1,
1539                                         // left
1540                                         1-tz2, 1-ty2, 1-tz1, 1-ty1,
1541                                         // back
1542                                         1-tx2, 1-ty2, 1-tx1, 1-ty1,
1543                                         // front
1544                                         tx1, 1-ty2, tx2, 1-ty1,
1545                                 };
1546                                 makeCuboid(&collector, box, tiles, 6, c, txc);
1547                         }
1548                 break;}
1549                 }
1550         }
1551 }
1552