]> git.lizzy.rs Git - minetest-m13.git/blob - src/content_mapblock.cpp
Update code style to C++11
[minetest-m13.git] / src / content_mapblock.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
14
15 You should have received a copy of the GNU 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
29 // Create a cuboid.
30 //  collector - the MeshCollector for the resulting polygons
31 //  box       - the position and size of the box
32 //  materials - the materials to use (for all 6 faces)
33 //  pa        - texture atlas pointers for the materials
34 //  matcount  - number of entries in "materials" and "pa", 1<=matcount<=6
35 //  c         - vertex colour - used for all
36 //  txc       - texture coordinates - this is a list of texture coordinates
37 //              for the opposite corners of each face - therefore, there
38 //              should be (2+2)*6=24 values in the list. Alternatively, pass
39 //              NULL to use the entire texture for each face. The order of
40 //              the faces in the list is up-down-right-left-back-front
41 //              (compatible with ContentFeatures). If you specified 0,0,1,1
42 //              for each face, that would be the same as passing NULL.
43 void makeCuboid(MeshCollector *collector, const aabb3f &box,
44         const video::SMaterial *materials, const AtlasPointer *pa, int matcount,        
45         video::SColor &c, const f32* txc)
46 {
47         assert(matcount >= 1);
48
49         v3f min = box.MinEdge;
50         v3f max = box.MaxEdge;
51
52         if(txc == NULL)
53         {
54                 static const f32 txc_default[24] = {
55                         0,0,1,1,
56                         0,0,1,1,
57                         0,0,1,1,
58                         0,0,1,1,
59                         0,0,1,1,
60                         0,0,1,1
61                 };
62                 txc = txc_default;
63         }
64
65         video::S3DVertex vertices[24] =
66         {
67                 // up
68                 video::S3DVertex(min.X,max.Y,max.Z, 0,1,0, c, txc[0],txc[1]),
69                 video::S3DVertex(max.X,max.Y,max.Z, 0,1,0, c, txc[2],txc[1]),
70                 video::S3DVertex(max.X,max.Y,min.Z, 0,1,0, c, txc[2],txc[3]),
71                 video::S3DVertex(min.X,max.Y,min.Z, 0,1,0, c, txc[0],txc[3]),
72                 // down
73                 video::S3DVertex(min.X,min.Y,min.Z, 0,-1,0, c, txc[4],txc[5]),
74                 video::S3DVertex(max.X,min.Y,min.Z, 0,-1,0, c, txc[6],txc[5]),
75                 video::S3DVertex(max.X,min.Y,max.Z, 0,-1,0, c, txc[6],txc[7]),
76                 video::S3DVertex(min.X,min.Y,max.Z, 0,-1,0, c, txc[4],txc[7]),
77                 // right
78                 video::S3DVertex(max.X,max.Y,min.Z, 1,0,0, c, txc[ 8],txc[9]),
79                 video::S3DVertex(max.X,max.Y,max.Z, 1,0,0, c, txc[10],txc[9]),
80                 video::S3DVertex(max.X,min.Y,max.Z, 1,0,0, c, txc[10],txc[11]),
81                 video::S3DVertex(max.X,min.Y,min.Z, 1,0,0, c, txc[ 8],txc[11]),
82                 // left
83                 video::S3DVertex(min.X,max.Y,max.Z, -1,0,0, c, txc[12],txc[13]),
84                 video::S3DVertex(min.X,max.Y,min.Z, -1,0,0, c, txc[14],txc[13]),
85                 video::S3DVertex(min.X,min.Y,min.Z, -1,0,0, c, txc[14],txc[15]),
86                 video::S3DVertex(min.X,min.Y,max.Z, -1,0,0, c, txc[12],txc[15]),
87                 // back
88                 video::S3DVertex(max.X,max.Y,max.Z, 0,0,1, c, txc[16],txc[17]),
89                 video::S3DVertex(min.X,max.Y,max.Z, 0,0,1, c, txc[18],txc[17]),
90                 video::S3DVertex(min.X,min.Y,max.Z, 0,0,1, c, txc[18],txc[19]),
91                 video::S3DVertex(max.X,min.Y,max.Z, 0,0,1, c, txc[16],txc[19]),
92                 // front
93                 video::S3DVertex(min.X,max.Y,min.Z, 0,0,-1, c, txc[20],txc[21]),
94                 video::S3DVertex(max.X,max.Y,min.Z, 0,0,-1, c, txc[22],txc[21]),
95                 video::S3DVertex(max.X,min.Y,min.Z, 0,0,-1, c, txc[22],txc[23]),
96                 video::S3DVertex(min.X,min.Y,min.Z, 0,0,-1, c, txc[20],txc[23]),
97         };
98
99         for(s32 j=0; j<24; j++)
100         {
101                 int matindex = MYMIN(j/4, matcount-1);
102                 vertices[j].TCoords *= pa[matindex].size;
103                 vertices[j].TCoords += pa[matindex].pos;
104         }
105
106         u16 indices[] = {0,1,2,2,3,0};
107
108         // Add to mesh collector
109         for(s32 j=0; j<24; j+=4)
110         {
111                 int matindex = MYMIN(j/4, matcount-1);
112                 collector->append(materials[matindex],
113                                 vertices+j, 4, indices, 6);
114         }
115 }
116
117 void mapblock_mesh_generate_special(MeshMakeData *data,
118                 MeshCollector &collector, IGameDef *gamedef)
119 {
120         INodeDefManager *nodedef = gamedef->ndef();
121         ITextureSource *tsrc = gamedef->getTextureSource();
122
123         // 0ms
124         //TimeTaker timer("mapblock_mesh_generate_special()");
125
126         /*
127                 Some settings
128         */
129         bool new_style_water = g_settings->getBool("new_style_water");
130         
131         float node_liquid_level = 1.0;
132         if(new_style_water)
133                 node_liquid_level = 0.85;
134         
135         v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
136
137         /*// General ground material for special output
138         // Texture is modified just before usage
139         video::SMaterial material_general;
140         material_general.setFlag(video::EMF_LIGHTING, false);
141         material_general.setFlag(video::EMF_BILINEAR_FILTER, false);
142         material_general.setFlag(video::EMF_FOG_ENABLE, true);
143         material_general.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;*/
144
145         for(s16 z=0; z<MAP_BLOCKSIZE; z++)
146         for(s16 y=0; y<MAP_BLOCKSIZE; y++)
147         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
148         {
149                 v3s16 p(x,y,z);
150
151                 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p);
152                 const ContentFeatures &f = nodedef->get(n);
153
154                 // Only solidness=0 stuff is drawn here
155                 if(f.solidness != 0)
156                         continue;
157                 
158                 switch(f.drawtype){
159                 default:
160                         infostream<<"Got "<<f.drawtype<<std::endl;
161                         assert(0);
162                         break;
163                 case NDT_AIRLIKE:
164                         break;
165                 case NDT_LIQUID:
166                 {
167                         /*
168                                 Add water sources to mesh if using new style
169                         */
170                         assert(nodedef->get(n).special_materials[0]);
171                         //assert(nodedef->get(n).special_materials[1]);
172                         assert(nodedef->get(n).special_aps[0]);
173
174                         video::SMaterial &liquid_material =
175                                         *nodedef->get(n).special_materials[0];
176                         /*video::SMaterial &liquid_material_bfculled =
177                                         *nodedef->get(n).special_materials[1];*/
178                         AtlasPointer &pa_liquid1 =
179                                         *nodedef->get(n).special_aps[0];
180
181                         bool top_is_air = false;
182                         MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
183                         if(n.getContent() == CONTENT_AIR)
184                                 top_is_air = true;
185                         
186                         if(top_is_air == false)
187                                 continue;
188
189                         u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio, nodedef));
190                         video::SColor c = MapBlock_LightColor(
191                                         nodedef->get(n).alpha, l);
192                         
193                         video::S3DVertex vertices[4] =
194                         {
195                                 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
196                                                 pa_liquid1.x0(), pa_liquid1.y1()),
197                                 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
198                                                 pa_liquid1.x1(), pa_liquid1.y1()),
199                                 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
200                                                 pa_liquid1.x1(), pa_liquid1.y0()),
201                                 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
202                                                 pa_liquid1.x0(), pa_liquid1.y0()),
203                         };
204
205                         for(s32 i=0; i<4; i++)
206                         {
207                                 vertices[i].Pos.Y += (-0.5+node_liquid_level)*BS;
208                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
209                         }
210
211                         u16 indices[] = {0,1,2,2,3,0};
212                         // Add to mesh collector
213                         collector.append(liquid_material, vertices, 4, indices, 6);
214                 break;}
215                 case NDT_FLOWINGLIQUID:
216                 {
217                         /*
218                                 Add flowing liquid to mesh
219                         */
220                         assert(nodedef->get(n).special_materials[0]);
221                         assert(nodedef->get(n).special_materials[1]);
222                         assert(nodedef->get(n).special_aps[0]);
223
224                         video::SMaterial &liquid_material =
225                                         *nodedef->get(n).special_materials[0];
226                         video::SMaterial &liquid_material_bfculled =
227                                         *nodedef->get(n).special_materials[1];
228                         AtlasPointer &pa_liquid1 =
229                                         *nodedef->get(n).special_aps[0];
230
231                         bool top_is_same_liquid = false;
232                         MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
233                         content_t c_flowing = nodedef->getId(nodedef->get(n).liquid_alternative_flowing);
234                         content_t c_source = nodedef->getId(nodedef->get(n).liquid_alternative_source);
235                         if(ntop.getContent() == c_flowing || ntop.getContent() == c_source)
236                                 top_is_same_liquid = true;
237                         
238                         u8 l = 0;
239                         // Use the light of the node on top if possible
240                         if(nodedef->get(ntop).param_type == CPT_LIGHT)
241                                 l = decode_light(ntop.getLightBlend(data->m_daynight_ratio, nodedef));
242                         // Otherwise use the light of this node (the liquid)
243                         else
244                                 l = decode_light(n.getLightBlend(data->m_daynight_ratio, nodedef));
245                         video::SColor c = MapBlock_LightColor(
246                                         nodedef->get(n).alpha, l);
247                         
248                         // Neighbor liquid levels (key = relative position)
249                         // Includes current node
250                         core::map<v3s16, f32> neighbor_levels;
251                         core::map<v3s16, content_t> neighbor_contents;
252                         core::map<v3s16, u8> neighbor_flags;
253                         const u8 neighborflag_top_is_same_liquid = 0x01;
254                         v3s16 neighbor_dirs[9] = {
255                                 v3s16(0,0,0),
256                                 v3s16(0,0,1),
257                                 v3s16(0,0,-1),
258                                 v3s16(1,0,0),
259                                 v3s16(-1,0,0),
260                                 v3s16(1,0,1),
261                                 v3s16(-1,0,-1),
262                                 v3s16(1,0,-1),
263                                 v3s16(-1,0,1),
264                         };
265                         for(u32 i=0; i<9; i++)
266                         {
267                                 content_t content = CONTENT_AIR;
268                                 float level = -0.5 * BS;
269                                 u8 flags = 0;
270                                 // Check neighbor
271                                 v3s16 p2 = p + neighbor_dirs[i];
272                                 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
273                                 if(n2.getContent() != CONTENT_IGNORE)
274                                 {
275                                         content = n2.getContent();
276
277                                         if(n2.getContent() == c_source)
278                                                 level = (-0.5+node_liquid_level) * BS;
279                                         else if(n2.getContent() == c_flowing)
280                                                 level = (-0.5 + ((float)(n2.param2&LIQUID_LEVEL_MASK)
281                                                                 + 0.5) / 8.0 * node_liquid_level) * BS;
282
283                                         // Check node above neighbor.
284                                         // NOTE: This doesn't get executed if neighbor
285                                         //       doesn't exist
286                                         p2.Y += 1;
287                                         n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
288                                         if(n2.getContent() == c_source ||
289                                                         n2.getContent() == c_flowing)
290                                                 flags |= neighborflag_top_is_same_liquid;
291                                 }
292                                 
293                                 neighbor_levels.insert(neighbor_dirs[i], level);
294                                 neighbor_contents.insert(neighbor_dirs[i], content);
295                                 neighbor_flags.insert(neighbor_dirs[i], flags);
296                         }
297
298                         // Corner heights (average between four liquids)
299                         f32 corner_levels[4];
300                         
301                         v3s16 halfdirs[4] = {
302                                 v3s16(0,0,0),
303                                 v3s16(1,0,0),
304                                 v3s16(1,0,1),
305                                 v3s16(0,0,1),
306                         };
307                         for(u32 i=0; i<4; i++)
308                         {
309                                 v3s16 cornerdir = halfdirs[i];
310                                 float cornerlevel = 0;
311                                 u32 valid_count = 0;
312                                 u32 air_count = 0;
313                                 for(u32 j=0; j<4; j++)
314                                 {
315                                         v3s16 neighbordir = cornerdir - halfdirs[j];
316                                         content_t content = neighbor_contents[neighbordir];
317                                         // If top is liquid, draw starting from top of node
318                                         if(neighbor_flags[neighbordir] &
319                                                         neighborflag_top_is_same_liquid)
320                                         {
321                                                 cornerlevel = 0.5*BS;
322                                                 valid_count = 1;
323                                                 break;
324                                         }
325                                         // Source is always the same height
326                                         else if(content == c_source)
327                                         {
328                                                 cornerlevel = (-0.5+node_liquid_level)*BS;
329                                                 valid_count = 1;
330                                                 break;
331                                         }
332                                         // Flowing liquid has level information
333                                         else if(content == c_flowing)
334                                         {
335                                                 cornerlevel += neighbor_levels[neighbordir];
336                                                 valid_count++;
337                                         }
338                                         else if(content == CONTENT_AIR)
339                                         {
340                                                 air_count++;
341                                         }
342                                 }
343                                 if(air_count >= 2)
344                                         cornerlevel = -0.5*BS;
345                                 else if(valid_count > 0)
346                                         cornerlevel /= valid_count;
347                                 corner_levels[i] = cornerlevel;
348                         }
349
350                         /*
351                                 Generate sides
352                         */
353
354                         v3s16 side_dirs[4] = {
355                                 v3s16(1,0,0),
356                                 v3s16(-1,0,0),
357                                 v3s16(0,0,1),
358                                 v3s16(0,0,-1),
359                         };
360                         s16 side_corners[4][2] = {
361                                 {1, 2},
362                                 {3, 0},
363                                 {2, 3},
364                                 {0, 1},
365                         };
366                         for(u32 i=0; i<4; i++)
367                         {
368                                 v3s16 dir = side_dirs[i];
369
370                                 /*
371                                         If our topside is liquid and neighbor's topside
372                                         is liquid, don't draw side face
373                                 */
374                                 if(top_is_same_liquid &&
375                                                 neighbor_flags[dir] & neighborflag_top_is_same_liquid)
376                                         continue;
377
378                                 content_t neighbor_content = neighbor_contents[dir];
379                                 const ContentFeatures &n_feat = nodedef->get(neighbor_content);
380                                 
381                                 // Don't draw face if neighbor is blocking the view
382                                 if(n_feat.solidness == 2)
383                                         continue;
384                                 
385                                 bool neighbor_is_same_liquid = (neighbor_content == c_source
386                                                 || neighbor_content == c_flowing);
387                                 
388                                 // Don't draw any faces if neighbor same is liquid and top is
389                                 // same liquid
390                                 if(neighbor_is_same_liquid == true
391                                                 && top_is_same_liquid == false)
392                                         continue;
393
394                                 // Use backface culled material if neighbor doesn't have a
395                                 // solidness of 0
396                                 video::SMaterial *current_material = &liquid_material;
397                                 if(n_feat.solidness != 0 || n_feat.visual_solidness != 0)
398                                         current_material = &liquid_material_bfculled;
399                                 
400                                 video::S3DVertex vertices[4] =
401                                 {
402                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
403                                                         pa_liquid1.x0(), pa_liquid1.y1()),
404                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
405                                                         pa_liquid1.x1(), pa_liquid1.y1()),
406                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
407                                                         pa_liquid1.x1(), pa_liquid1.y0()),
408                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
409                                                         pa_liquid1.x0(), pa_liquid1.y0()),
410                                 };
411                                 
412                                 /*
413                                         If our topside is liquid, set upper border of face
414                                         at upper border of node
415                                 */
416                                 if(top_is_same_liquid)
417                                 {
418                                         vertices[2].Pos.Y = 0.5*BS;
419                                         vertices[3].Pos.Y = 0.5*BS;
420                                 }
421                                 /*
422                                         Otherwise upper position of face is corner levels
423                                 */
424                                 else
425                                 {
426                                         vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
427                                         vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
428                                 }
429                                 
430                                 /*
431                                         If neighbor is liquid, lower border of face is corner
432                                         liquid levels
433                                 */
434                                 if(neighbor_is_same_liquid)
435                                 {
436                                         vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
437                                         vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
438                                 }
439                                 /*
440                                         If neighbor is not liquid, lower border of face is
441                                         lower border of node
442                                 */
443                                 else
444                                 {
445                                         vertices[0].Pos.Y = -0.5*BS;
446                                         vertices[1].Pos.Y = -0.5*BS;
447                                 }
448                                 
449                                 for(s32 j=0; j<4; j++)
450                                 {
451                                         if(dir == v3s16(0,0,1))
452                                                 vertices[j].Pos.rotateXZBy(0);
453                                         if(dir == v3s16(0,0,-1))
454                                                 vertices[j].Pos.rotateXZBy(180);
455                                         if(dir == v3s16(-1,0,0))
456                                                 vertices[j].Pos.rotateXZBy(90);
457                                         if(dir == v3s16(1,0,-0))
458                                                 vertices[j].Pos.rotateXZBy(-90);
459                                                 
460                                         // Do this to not cause glitches when two liquids are
461                                         // side-by-side
462                                         /*if(neighbor_is_same_liquid == false){
463                                                 vertices[j].Pos.X *= 0.98;
464                                                 vertices[j].Pos.Z *= 0.98;
465                                         }*/
466
467                                         vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
468                                 }
469
470                                 u16 indices[] = {0,1,2,2,3,0};
471                                 // Add to mesh collector
472                                 collector.append(*current_material, vertices, 4, indices, 6);
473                         }
474                         
475                         /*
476                                 Generate top side, if appropriate
477                         */
478                         
479                         if(top_is_same_liquid == false)
480                         {
481                                 video::S3DVertex vertices[4] =
482                                 {
483                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
484                                                         pa_liquid1.x0(), pa_liquid1.y1()),
485                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
486                                                         pa_liquid1.x1(), pa_liquid1.y1()),
487                                         video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
488                                                         pa_liquid1.x1(), pa_liquid1.y0()),
489                                         video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
490                                                         pa_liquid1.x0(), pa_liquid1.y0()),
491                                 };
492                                 
493                                 // This fixes a strange bug
494                                 s32 corner_resolve[4] = {3,2,1,0};
495
496                                 for(s32 i=0; i<4; i++)
497                                 {
498                                         //vertices[i].Pos.Y += liquid_level;
499                                         //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
500                                         s32 j = corner_resolve[i];
501                                         vertices[i].Pos.Y += corner_levels[j];
502                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
503                                 }
504
505                                 u16 indices[] = {0,1,2,2,3,0};
506                                 // Add to mesh collector
507                                 collector.append(liquid_material, vertices, 4, indices, 6);
508                         }
509                 break;}
510                 case NDT_GLASSLIKE:
511                 {
512                         video::SMaterial material_glass;
513                         material_glass.setFlag(video::EMF_LIGHTING, false);
514                         material_glass.setFlag(video::EMF_BILINEAR_FILTER, false);
515                         material_glass.setFlag(video::EMF_FOG_ENABLE, true);
516                         material_glass.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
517                         TileSpec tile_glass = getNodeTile(n, p, v3s16(0,0,0),
518                                         &data->m_temp_mods, tsrc, nodedef);
519                         AtlasPointer pa_glass = tile_glass.texture;
520                         material_glass.setTexture(0, pa_glass.atlas);
521
522                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef)));
523                         video::SColor c = MapBlock_LightColor(255, l);
524
525                         for(u32 j=0; j<6; j++)
526                         {
527                                 // Check this neighbor
528                                 v3s16 n2p = blockpos_nodes + p + g_6dirs[j];
529                                 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
530                                 // Don't make face if neighbor is of same type
531                                 if(n2.getContent() == n.getContent())
532                                         continue;
533
534                                 // The face at Z+
535                                 video::S3DVertex vertices[4] =
536                                 {
537                                         video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
538                                                 pa_glass.x0(), pa_glass.y1()),
539                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
540                                                 pa_glass.x1(), pa_glass.y1()),
541                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
542                                                 pa_glass.x1(), pa_glass.y0()),
543                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
544                                                 pa_glass.x0(), pa_glass.y0()),
545                                 };
546                                 
547                                 // Rotations in the g_6dirs format
548                                 if(j == 0) // Z+
549                                         for(u16 i=0; i<4; i++)
550                                                 vertices[i].Pos.rotateXZBy(0);
551                                 else if(j == 1) // Y+
552                                         for(u16 i=0; i<4; i++)
553                                                 vertices[i].Pos.rotateYZBy(-90);
554                                 else if(j == 2) // X+
555                                         for(u16 i=0; i<4; i++)
556                                                 vertices[i].Pos.rotateXZBy(-90);
557                                 else if(j == 3) // Z-
558                                         for(u16 i=0; i<4; i++)
559                                                 vertices[i].Pos.rotateXZBy(180);
560                                 else if(j == 4) // Y-
561                                         for(u16 i=0; i<4; i++)
562                                                 vertices[i].Pos.rotateYZBy(90);
563                                 else if(j == 5) // X-
564                                         for(u16 i=0; i<4; i++)
565                                                 vertices[i].Pos.rotateXZBy(90);
566
567                                 for(u16 i=0; i<4; i++){
568                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
569                                 }
570
571                                 u16 indices[] = {0,1,2,2,3,0};
572                                 // Add to mesh collector
573                                 collector.append(material_glass, vertices, 4, indices, 6);
574                         }
575                 break;}
576                 case NDT_ALLFACES:
577                 {
578                         video::SMaterial material_leaves1;
579                         material_leaves1.setFlag(video::EMF_LIGHTING, false);
580                         material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
581                         material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
582                         material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
583                         TileSpec tile_leaves1 = getNodeTile(n, p, v3s16(0,0,0),
584                                         &data->m_temp_mods, tsrc, nodedef);
585                         AtlasPointer pa_leaves1 = tile_leaves1.texture;
586                         material_leaves1.setTexture(0, pa_leaves1.atlas);
587
588                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef)));
589                         video::SColor c = MapBlock_LightColor(255, l);
590
591                         v3f pos = intToFloat(p+blockpos_nodes, BS);
592                         aabb3f box(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2);
593                         box.MinEdge += pos;
594                         box.MaxEdge += pos;
595                         makeCuboid(&collector, box,
596                                         &material_leaves1, &pa_leaves1, 1,
597                                         c, NULL);
598                 break;}
599                 case NDT_ALLFACES_OPTIONAL:
600                         // This is always pre-converted to something else
601                         assert(0);
602                         break;
603                 case NDT_TORCHLIKE:
604                 {
605                         v3s16 dir = n.getWallMountedDir(nodedef);
606                         
607                         AtlasPointer ap(0);
608                         if(dir == v3s16(0,-1,0)){
609                                 ap = f.tiles[0].texture; // floor
610                         } else if(dir == v3s16(0,1,0)){
611                                 ap = f.tiles[1].texture; // ceiling
612                         // For backwards compatibility
613                         } else if(dir == v3s16(0,0,0)){
614                                 ap = f.tiles[0].texture; // floor
615                         } else {
616                                 ap = f.tiles[2].texture; // side
617                         }
618
619                         // Set material
620                         video::SMaterial material;
621                         material.setFlag(video::EMF_LIGHTING, false);
622                         material.setFlag(video::EMF_BACK_FACE_CULLING, false);
623                         material.setFlag(video::EMF_BILINEAR_FILTER, false);
624                         material.setFlag(video::EMF_FOG_ENABLE, true);
625                         //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
626                         material.MaterialType
627                                         = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
628                         material.setTexture(0, ap.atlas);
629
630                         video::SColor c(255,255,255,255);
631
632                         // Wall at X+ of node
633                         video::S3DVertex vertices[4] =
634                         {
635                                 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c,
636                                                 ap.x0(), ap.y1()),
637                                 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c,
638                                                 ap.x1(), ap.y1()),
639                                 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c,
640                                                 ap.x1(), ap.y0()),
641                                 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c,
642                                                 ap.x0(), ap.y0()),
643                         };
644
645                         for(s32 i=0; i<4; i++)
646                         {
647                                 if(dir == v3s16(1,0,0))
648                                         vertices[i].Pos.rotateXZBy(0);
649                                 if(dir == v3s16(-1,0,0))
650                                         vertices[i].Pos.rotateXZBy(180);
651                                 if(dir == v3s16(0,0,1))
652                                         vertices[i].Pos.rotateXZBy(90);
653                                 if(dir == v3s16(0,0,-1))
654                                         vertices[i].Pos.rotateXZBy(-90);
655                                 if(dir == v3s16(0,-1,0))
656                                         vertices[i].Pos.rotateXZBy(45);
657                                 if(dir == v3s16(0,1,0))
658                                         vertices[i].Pos.rotateXZBy(-45);
659
660                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
661                         }
662
663                         u16 indices[] = {0,1,2,2,3,0};
664                         // Add to mesh collector
665                         collector.append(material, vertices, 4, indices, 6);
666                 break;}
667                 case NDT_SIGNLIKE:
668                 {
669                         // Set material
670                         video::SMaterial material;
671                         material.setFlag(video::EMF_LIGHTING, false);
672                         material.setFlag(video::EMF_BACK_FACE_CULLING, false);
673                         material.setFlag(video::EMF_BILINEAR_FILTER, false);
674                         material.setFlag(video::EMF_FOG_ENABLE, true);
675                         material.MaterialType
676                                         = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
677                         AtlasPointer ap = f.tiles[0].texture;
678                         material.setTexture(0, ap.atlas);
679
680                         u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio, nodedef));
681                         video::SColor c = MapBlock_LightColor(255, l);
682                                 
683                         float d = (float)BS/16;
684                         // Wall at X+ of node
685                         video::S3DVertex vertices[4] =
686                         {
687                                 video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c,
688                                                 ap.x0(), ap.y0()),
689                                 video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c,
690                                                 ap.x1(), ap.y0()),
691                                 video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c,
692                                                 ap.x1(), ap.y1()),
693                                 video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c,
694                                                 ap.x0(), ap.y1()),
695                         };
696
697                         v3s16 dir = n.getWallMountedDir(nodedef);
698
699                         for(s32 i=0; i<4; i++)
700                         {
701                                 if(dir == v3s16(1,0,0))
702                                         vertices[i].Pos.rotateXZBy(0);
703                                 if(dir == v3s16(-1,0,0))
704                                         vertices[i].Pos.rotateXZBy(180);
705                                 if(dir == v3s16(0,0,1))
706                                         vertices[i].Pos.rotateXZBy(90);
707                                 if(dir == v3s16(0,0,-1))
708                                         vertices[i].Pos.rotateXZBy(-90);
709                                 if(dir == v3s16(0,-1,0))
710                                         vertices[i].Pos.rotateXYBy(-90);
711                                 if(dir == v3s16(0,1,0))
712                                         vertices[i].Pos.rotateXYBy(90);
713
714                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
715                         }
716
717                         u16 indices[] = {0,1,2,2,3,0};
718                         // Add to mesh collector
719                         collector.append(material, vertices, 4, indices, 6);
720                 break;}
721                 case NDT_PLANTLIKE:
722                 {
723                         video::SMaterial material_papyrus;
724                         material_papyrus.setFlag(video::EMF_LIGHTING, false);
725                         material_papyrus.setFlag(video::EMF_BILINEAR_FILTER, false);
726                         material_papyrus.setFlag(video::EMF_FOG_ENABLE, true);
727                         material_papyrus.MaterialType=video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
728                         AtlasPointer pa_papyrus = f.tiles[0].texture;
729                         material_papyrus.setTexture(0, pa_papyrus.atlas);
730                         
731                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef)));
732                         video::SColor c = MapBlock_LightColor(255, l);
733
734                         for(u32 j=0; j<4; j++)
735                         {
736                                 video::S3DVertex vertices[4] =
737                                 {
738                                         video::S3DVertex(-BS/2*f.visual_scale,-BS/2,0, 0,0,0, c,
739                                                 pa_papyrus.x0(), pa_papyrus.y1()),
740                                         video::S3DVertex( BS/2*f.visual_scale,-BS/2,0, 0,0,0, c,
741                                                 pa_papyrus.x1(), pa_papyrus.y1()),
742                                         video::S3DVertex( BS/2*f.visual_scale,
743                                                 -BS/2 + f.visual_scale*BS,0, 0,0,0, c,
744                                                 pa_papyrus.x1(), pa_papyrus.y0()),
745                                         video::S3DVertex(-BS/2*f.visual_scale,
746                                                 -BS/2 + f.visual_scale*BS,0, 0,0,0, c,
747                                                 pa_papyrus.x0(), pa_papyrus.y0()),
748                                 };
749
750                                 if(j == 0)
751                                 {
752                                         for(u16 i=0; i<4; i++)
753                                                 vertices[i].Pos.rotateXZBy(45);
754                                 }
755                                 else if(j == 1)
756                                 {
757                                         for(u16 i=0; i<4; i++)
758                                                 vertices[i].Pos.rotateXZBy(-45);
759                                 }
760                                 else if(j == 2)
761                                 {
762                                         for(u16 i=0; i<4; i++)
763                                                 vertices[i].Pos.rotateXZBy(135);
764                                 }
765                                 else if(j == 3)
766                                 {
767                                         for(u16 i=0; i<4; i++)
768                                                 vertices[i].Pos.rotateXZBy(-135);
769                                 }
770
771                                 for(u16 i=0; i<4; i++)
772                                 {
773                                         vertices[i].Pos *= f.visual_scale;
774                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
775                                 }
776
777                                 u16 indices[] = {0,1,2,2,3,0};
778                                 // Add to mesh collector
779                                 collector.append(material_papyrus, vertices, 4, indices, 6);
780                         }
781                 break;}
782                 case NDT_FENCELIKE:
783                 {
784                         video::SMaterial material_wood;
785                         material_wood.setFlag(video::EMF_LIGHTING, false);
786                         material_wood.setFlag(video::EMF_BILINEAR_FILTER, false);
787                         material_wood.setFlag(video::EMF_FOG_ENABLE, true);
788                         material_wood.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
789                         TileSpec tile_wood = getNodeTile(n, p, v3s16(0,0,0),
790                                         &data->m_temp_mods, tsrc, nodedef);
791                         AtlasPointer pa_wood = tile_wood.texture;
792                         material_wood.setTexture(0, pa_wood.atlas);
793
794                         video::SMaterial material_wood_nomod;
795                         material_wood_nomod.setFlag(video::EMF_LIGHTING, false);
796                         material_wood_nomod.setFlag(video::EMF_BILINEAR_FILTER, false);
797                         material_wood_nomod.setFlag(video::EMF_FOG_ENABLE, true);
798                         material_wood_nomod.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
799
800                         TileSpec tile_wood_nomod = getNodeTile(n, p, v3s16(0,0,0),
801                                         NULL, tsrc, nodedef);
802                         AtlasPointer pa_wood_nomod = tile_wood_nomod.texture;
803                         material_wood_nomod.setTexture(0, pa_wood_nomod.atlas);
804
805                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef)));
806                         video::SColor c = MapBlock_LightColor(255, l);
807
808                         const f32 post_rad=(f32)BS/10;
809                         const f32 bar_rad=(f32)BS/20;
810                         const f32 bar_len=(f32)(BS/2)-post_rad;
811
812                         v3f pos = intToFloat(p+blockpos_nodes, BS);
813
814                         // The post - always present
815                         aabb3f post(-post_rad,-BS/2,-post_rad,post_rad,BS/2,post_rad);
816                         post.MinEdge += pos;
817                         post.MaxEdge += pos;
818                         f32 postuv[24]={
819                                         0.4,0.4,0.6,0.6,
820                                         0.4,0.4,0.6,0.6,
821                                         0.35,0,0.65,1,
822                                         0.35,0,0.65,1,
823                                         0.35,0,0.65,1,
824                                         0.35,0,0.65,1};
825                         makeCuboid(&collector, post, &material_wood,
826                                         &pa_wood, 1, c, postuv);
827
828                         // Now a section of fence, +X, if there's a post there
829                         v3s16 p2 = p;
830                         p2.X++;
831                         MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
832                         const ContentFeatures *f2 = &nodedef->get(n2);
833                         if(f2->drawtype == NDT_FENCELIKE)
834                         {
835                                 aabb3f bar(-bar_len+BS/2,-bar_rad+BS/4,-bar_rad,
836                                                 bar_len+BS/2,bar_rad+BS/4,bar_rad);
837                                 bar.MinEdge += pos;
838                                 bar.MaxEdge += pos;
839                                 f32 xrailuv[24]={
840                                         0,0.4,1,0.6,
841                                         0,0.4,1,0.6,
842                                         0,0.4,1,0.6,
843                                         0,0.4,1,0.6,
844                                         0,0.4,1,0.6,
845                                         0,0.4,1,0.6};
846                                 makeCuboid(&collector, bar, &material_wood_nomod,
847                                                 &pa_wood_nomod, 1, c, xrailuv);
848                                 bar.MinEdge.Y -= BS/2;
849                                 bar.MaxEdge.Y -= BS/2;
850                                 makeCuboid(&collector, bar, &material_wood_nomod,
851                                                 &pa_wood_nomod, 1, c, xrailuv);
852                         }
853
854                         // Now a section of fence, +Z, if there's a post there
855                         p2 = p;
856                         p2.Z++;
857                         n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
858                         f2 = &nodedef->get(n2);
859                         if(f2->drawtype == NDT_FENCELIKE)
860                         {
861                                 aabb3f bar(-bar_rad,-bar_rad+BS/4,-bar_len+BS/2,
862                                                 bar_rad,bar_rad+BS/4,bar_len+BS/2);
863                                 bar.MinEdge += pos;
864                                 bar.MaxEdge += pos;
865                                 f32 zrailuv[24]={
866                                         0,0.4,1,0.6,
867                                         0,0.4,1,0.6,
868                                         0,0.4,1,0.6,
869                                         0,0.4,1,0.6,
870                                         0,0.4,1,0.6,
871                                         0,0.4,1,0.6};
872
873                                 makeCuboid(&collector, bar, &material_wood_nomod,
874                                                 &pa_wood_nomod, 1, c, zrailuv);
875                                 bar.MinEdge.Y -= BS/2;
876                                 bar.MaxEdge.Y -= BS/2;
877                                 makeCuboid(&collector, bar, &material_wood_nomod,
878                                                 &pa_wood_nomod, 1, c, zrailuv);
879                         }
880                 break;}
881                 case NDT_RAILLIKE:
882                 {
883                         bool is_rail_x [] = { false, false };  /* x-1, x+1 */
884                         bool is_rail_z [] = { false, false };  /* z-1, z+1 */
885
886                         MapNode n_minus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1,y,z));
887                         MapNode n_plus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1,y,z));
888                         MapNode n_minus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z-1));
889                         MapNode n_plus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z+1));
890                         
891                         content_t thiscontent = n.getContent();
892                         if(n_minus_x.getContent() == thiscontent)
893                                 is_rail_x[0] = true;
894                         if(n_plus_x.getContent() == thiscontent)
895                                 is_rail_x[1] = true;
896                         if(n_minus_z.getContent() == thiscontent)
897                                 is_rail_z[0] = true;
898                         if(n_plus_z.getContent() == thiscontent)
899                                 is_rail_z[1] = true;
900
901                         int adjacencies = is_rail_x[0] + is_rail_x[1] + is_rail_z[0] + is_rail_z[1];
902
903                         // Assign textures
904                         AtlasPointer ap = f.tiles[0].texture; // straight
905                         if(adjacencies < 2)
906                                 ap = f.tiles[0].texture; // straight
907                         else if(adjacencies == 2)
908                         {
909                                 if((is_rail_x[0] && is_rail_x[1]) || (is_rail_z[0] && is_rail_z[1]))
910                                         ap = f.tiles[0].texture; // straight
911                                 else
912                                         ap = f.tiles[1].texture; // curved
913                         }
914                         else if(adjacencies == 3)
915                                 ap = f.tiles[2].texture; // t-junction
916                         else if(adjacencies == 4)
917                                 ap = f.tiles[3].texture; // crossing
918                         
919                         video::SMaterial material_rail;
920                         material_rail.setFlag(video::EMF_LIGHTING, false);
921                         material_rail.setFlag(video::EMF_BACK_FACE_CULLING, false);
922                         material_rail.setFlag(video::EMF_BILINEAR_FILTER, false);
923                         material_rail.setFlag(video::EMF_FOG_ENABLE, true);
924                         material_rail.MaterialType
925                                         = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
926                         material_rail.setTexture(0, ap.atlas);
927
928                         u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio, nodedef));
929                         video::SColor c = MapBlock_LightColor(255, l);
930
931                         float d = (float)BS/16;
932                         video::S3DVertex vertices[4] =
933                         {
934                                 video::S3DVertex(-BS/2,-BS/2+d,-BS/2, 0,0,0, c,
935                                                 ap.x0(), ap.y1()),
936                                 video::S3DVertex(BS/2,-BS/2+d,-BS/2, 0,0,0, c,
937                                                 ap.x1(), ap.y1()),
938                                 video::S3DVertex(BS/2,-BS/2+d,BS/2, 0,0,0, c,
939                                                 ap.x1(), ap.y0()),
940                                 video::S3DVertex(-BS/2,-BS/2+d,BS/2, 0,0,0, c,
941                                                 ap.x0(), ap.y0()),
942                         };
943
944                         // Rotate textures
945                         int angle = 0;
946
947                         if(adjacencies == 1)
948                         {
949                                 if(is_rail_x[0] || is_rail_x[1])
950                                         angle = 90;
951                         }
952                         else if(adjacencies == 2)
953                         {
954                                 if(is_rail_x[0] && is_rail_x[1])
955                                         angle = 90;
956                                 else if(is_rail_x[0] && is_rail_z[0])
957                                         angle = 270;
958                                 else if(is_rail_x[0] && is_rail_z[1])
959                                         angle = 180;
960                                 else if(is_rail_x[1] && is_rail_z[1])
961                                         angle = 90;
962                         }
963                         else if(adjacencies == 3)
964                         {
965                                 if(!is_rail_x[0])
966                                         angle=0;
967                                 if(!is_rail_x[1])
968                                         angle=180;
969                                 if(!is_rail_z[0])
970                                         angle=90;
971                                 if(!is_rail_z[1])
972                                         angle=270;
973                         }
974
975                         if(angle != 0) {
976                                 for(u16 i=0; i<4; i++)
977                                         vertices[i].Pos.rotateXZBy(angle);
978                         }
979
980                         for(s32 i=0; i<4; i++)
981                         {
982                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
983                         }
984
985                         u16 indices[] = {0,1,2,2,3,0};
986                         collector.append(material_rail, vertices, 4, indices, 6);
987                 break;}
988                 }
989         }
990 }
991