Commented out debug statements again
[oweals/minetest.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 #include "content_mapnode.h"
22 #include "main.h" // For g_settings and g_texturesource
23 #include "mineral.h"
24 #include "mapblock_mesh.h" // For MapBlock_LightColor()
25
26 #ifndef SERVER
27 // Create a cuboid.
28 //  material  - the material to use (for all 6 faces)
29 //  collector - the MeshCollector for the resulting polygons
30 //  pa        - texture atlas pointer for the material
31 //  c         - vertex colour - used for all
32 //  pos       - the position of the centre of the cuboid
33 //  rz,ry,rz  - the radius of the cuboid in each dimension
34 //  txc       - texture coordinates - this is a list of texture coordinates
35 //              for the opposite corners of each face - therefore, there
36 //              should be (2+2)*6=24 values in the list. Alternatively, pass
37 //              NULL to use the entire texture for each face. The order of
38 //              the faces in the list is top-backi-right-front-left-bottom
39 //              If you specified 0,0,1,1 for each face, that would be the
40 //              same as passing NULL.
41 void makeCuboid(video::SMaterial &material, MeshCollector *collector,
42         AtlasPointer* pa, video::SColor &c,
43         v3f &pos, f32 rx, f32 ry, f32 rz, f32* txc)
44 {
45         f32 tu0=pa->x0();
46         f32 tu1=pa->x1();
47         f32 tv0=pa->y0();
48         f32 tv1=pa->y1();
49         f32 txus=tu1-tu0;
50         f32 txvs=tv1-tv0;
51
52         video::S3DVertex v[4] =
53         {
54                 video::S3DVertex(0,0,0, 0,0,0, c, tu0, tv1),
55                 video::S3DVertex(0,0,0, 0,0,0, c, tu1, tv1),
56                 video::S3DVertex(0,0,0, 0,0,0, c, tu1, tv0),
57                 video::S3DVertex(0,0,0, 0,0,0, c, tu0, tv0)
58         };
59
60         for(int i=0;i<6;i++)
61         {
62                 switch(i)
63                 {
64                         case 0: // top
65                                 v[0].Pos.X=-rx; v[0].Pos.Y= ry; v[0].Pos.Z=-rz;
66                                 v[1].Pos.X=-rx; v[1].Pos.Y= ry; v[1].Pos.Z= rz;
67                                 v[2].Pos.X= rx; v[2].Pos.Y= ry; v[2].Pos.Z= rz;
68                                 v[3].Pos.X= rx; v[3].Pos.Y= ry, v[3].Pos.Z=-rz;
69                                 break;
70                         case 1: // back
71                                 v[0].Pos.X=-rx; v[0].Pos.Y= ry; v[0].Pos.Z=-rz;
72                                 v[1].Pos.X= rx; v[1].Pos.Y= ry; v[1].Pos.Z=-rz;
73                                 v[2].Pos.X= rx; v[2].Pos.Y=-ry; v[2].Pos.Z=-rz;
74                                 v[3].Pos.X=-rx; v[3].Pos.Y=-ry, v[3].Pos.Z=-rz;
75                                 break;
76                         case 2: //right
77                                 v[0].Pos.X= rx; v[0].Pos.Y= ry; v[0].Pos.Z=-rz;
78                                 v[1].Pos.X= rx; v[1].Pos.Y= ry; v[1].Pos.Z= rz;
79                                 v[2].Pos.X= rx; v[2].Pos.Y=-ry; v[2].Pos.Z= rz;
80                                 v[3].Pos.X= rx; v[3].Pos.Y=-ry, v[3].Pos.Z=-rz;
81                                 break;
82                         case 3: // front
83                                 v[0].Pos.X= rx; v[0].Pos.Y= ry; v[0].Pos.Z= rz;
84                                 v[1].Pos.X=-rx; v[1].Pos.Y= ry; v[1].Pos.Z= rz;
85                                 v[2].Pos.X=-rx; v[2].Pos.Y=-ry; v[2].Pos.Z= rz;
86                                 v[3].Pos.X= rx; v[3].Pos.Y=-ry, v[3].Pos.Z= rz;
87                                 break;
88                         case 4: // left
89                                 v[0].Pos.X=-rx; v[0].Pos.Y= ry; v[0].Pos.Z= rz;
90                                 v[1].Pos.X=-rx; v[1].Pos.Y= ry; v[1].Pos.Z=-rz;
91                                 v[2].Pos.X=-rx; v[2].Pos.Y=-ry; v[2].Pos.Z=-rz;
92                                 v[3].Pos.X=-rx; v[3].Pos.Y=-ry, v[3].Pos.Z= rz;
93                                 break;
94                         case 5: // bottom
95                                 v[0].Pos.X= rx; v[0].Pos.Y=-ry; v[0].Pos.Z= rz;
96                                 v[1].Pos.X=-rx; v[1].Pos.Y=-ry; v[1].Pos.Z= rz;
97                                 v[2].Pos.X=-rx; v[2].Pos.Y=-ry; v[2].Pos.Z=-rz;
98                                 v[3].Pos.X= rx; v[3].Pos.Y=-ry, v[3].Pos.Z=-rz;
99                                 break;
100                 }
101
102                 if(txc!=NULL)
103                 {
104                         v[0].TCoords.X=tu0+txus*txc[0]; v[0].TCoords.Y=tv0+txvs*txc[3];
105                         v[1].TCoords.X=tu0+txus*txc[2]; v[1].TCoords.Y=tv0+txvs*txc[3];
106                         v[2].TCoords.X=tu0+txus*txc[2]; v[2].TCoords.Y=tv0+txvs*txc[1];
107                         v[3].TCoords.X=tu0+txus*txc[0]; v[3].TCoords.Y=tv0+txvs*txc[1];
108                         txc+=4;
109                 }
110
111                 for(u16 i=0; i<4; i++)
112                         v[i].Pos += pos;
113                 u16 indices[] = {0,1,2,2,3,0};
114                 collector->append(material, v, 4, indices, 6);
115
116         }
117
118 }
119 #endif
120
121 #ifndef SERVER
122 void mapblock_mesh_generate_special(MeshMakeData *data,
123                 MeshCollector &collector)
124 {
125         // 0ms
126         //TimeTaker timer("mapblock_mesh_generate_special()");
127
128         /*
129                 Some settings
130         */
131         bool new_style_water = g_settings.getBool("new_style_water");
132         bool new_style_leaves = g_settings.getBool("new_style_leaves");
133         //bool smooth_lighting = g_settings.getBool("smooth_lighting");
134         bool invisible_stone = g_settings.getBool("invisible_stone");
135         
136         float node_liquid_level = 1.0;
137         if(new_style_water)
138                 node_liquid_level = 0.85;
139         
140         v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
141
142         // New-style leaves material
143         video::SMaterial material_leaves1;
144         material_leaves1.setFlag(video::EMF_LIGHTING, false);
145         //material_leaves1.setFlag(video::EMF_BACK_FACE_CULLING, false);
146         material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
147         material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
148         material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
149         AtlasPointer pa_leaves1 = g_texturesource->getTexture(
150                         g_texturesource->getTextureId("leaves.png"));
151         material_leaves1.setTexture(0, pa_leaves1.atlas);
152
153         // Glass material
154         video::SMaterial material_glass;
155         material_glass.setFlag(video::EMF_LIGHTING, false);
156         material_glass.setFlag(video::EMF_BILINEAR_FILTER, false);
157         material_glass.setFlag(video::EMF_FOG_ENABLE, true);
158         material_glass.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
159         AtlasPointer pa_glass = g_texturesource->getTexture(
160                         g_texturesource->getTextureId("glass.png"));
161         material_glass.setTexture(0, pa_glass.atlas);
162
163         // Wood material
164         video::SMaterial material_wood;
165         material_wood.setFlag(video::EMF_LIGHTING, false);
166         material_wood.setFlag(video::EMF_BILINEAR_FILTER, false);
167         material_wood.setFlag(video::EMF_FOG_ENABLE, true);
168         material_wood.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
169         AtlasPointer pa_wood = g_texturesource->getTexture(
170                         g_texturesource->getTextureId("wood.png"));
171         material_wood.setTexture(0, pa_wood.atlas);
172
173         // General ground material for special output
174         // Texture is modified just before usage
175         video::SMaterial material_general;
176         material_general.setFlag(video::EMF_LIGHTING, false);
177         material_general.setFlag(video::EMF_BILINEAR_FILTER, false);
178         material_general.setFlag(video::EMF_FOG_ENABLE, true);
179         material_general.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
180
181
182         // Papyrus material
183         video::SMaterial material_papyrus;
184         material_papyrus.setFlag(video::EMF_LIGHTING, false);
185         material_papyrus.setFlag(video::EMF_BILINEAR_FILTER, false);
186         material_papyrus.setFlag(video::EMF_FOG_ENABLE, true);
187         material_papyrus.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
188         AtlasPointer pa_papyrus = g_texturesource->getTexture(
189                         g_texturesource->getTextureId("papyrus.png"));
190         material_papyrus.setTexture(0, pa_papyrus.atlas);
191
192         // junglegrass material
193         video::SMaterial material_junglegrass;
194         material_junglegrass.setFlag(video::EMF_LIGHTING, false);
195         material_junglegrass.setFlag(video::EMF_BILINEAR_FILTER, false);
196         material_junglegrass.setFlag(video::EMF_FOG_ENABLE, true);
197         material_junglegrass.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
198         AtlasPointer pa_junglegrass = g_texturesource->getTexture(
199                         g_texturesource->getTextureId("junglegrass.png"));
200         material_junglegrass.setTexture(0, pa_junglegrass.atlas);
201
202         for(s16 z=0; z<MAP_BLOCKSIZE; z++)
203         for(s16 y=0; y<MAP_BLOCKSIZE; y++)
204         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
205         {
206                 v3s16 p(x,y,z);
207
208                 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p);
209                 
210                 /*
211                         Add torches to mesh
212                 */
213                 if(n.getContent() == CONTENT_TORCH)
214                 {
215                         video::SColor c(255,255,255,255);
216
217                         // Wall at X+ of node
218                         video::S3DVertex vertices[4] =
219                         {
220                                 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
221                                 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
222                                 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
223                                 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
224                         };
225
226                         v3s16 dir = unpackDir(n.param2);
227
228                         for(s32 i=0; i<4; i++)
229                         {
230                                 if(dir == v3s16(1,0,0))
231                                         vertices[i].Pos.rotateXZBy(0);
232                                 if(dir == v3s16(-1,0,0))
233                                         vertices[i].Pos.rotateXZBy(180);
234                                 if(dir == v3s16(0,0,1))
235                                         vertices[i].Pos.rotateXZBy(90);
236                                 if(dir == v3s16(0,0,-1))
237                                         vertices[i].Pos.rotateXZBy(-90);
238                                 if(dir == v3s16(0,-1,0))
239                                         vertices[i].Pos.rotateXZBy(45);
240                                 if(dir == v3s16(0,1,0))
241                                         vertices[i].Pos.rotateXZBy(-45);
242
243                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
244                         }
245
246                         // Set material
247                         video::SMaterial material;
248                         material.setFlag(video::EMF_LIGHTING, false);
249                         material.setFlag(video::EMF_BACK_FACE_CULLING, false);
250                         material.setFlag(video::EMF_BILINEAR_FILTER, false);
251                         //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
252                         material.MaterialType
253                                         = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
254
255                         if(dir == v3s16(0,-1,0))
256                                 material.setTexture(0,
257                                                 g_texturesource->getTextureRaw("torch_on_floor.png"));
258                         else if(dir == v3s16(0,1,0))
259                                 material.setTexture(0,
260                                                 g_texturesource->getTextureRaw("torch_on_ceiling.png"));
261                         // For backwards compatibility
262                         else if(dir == v3s16(0,0,0))
263                                 material.setTexture(0,
264                                                 g_texturesource->getTextureRaw("torch_on_floor.png"));
265                         else
266                                 material.setTexture(0, 
267                                                 g_texturesource->getTextureRaw("torch.png"));
268
269                         u16 indices[] = {0,1,2,2,3,0};
270                         // Add to mesh collector
271                         collector.append(material, vertices, 4, indices, 6);
272                 }
273                 /*
274                         Signs on walls
275                 */
276                 else if(n.getContent() == CONTENT_SIGN_WALL)
277                 {
278                         u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
279                         video::SColor c = MapBlock_LightColor(255, l);
280                                 
281                         float d = (float)BS/16;
282                         // Wall at X+ of node
283                         video::S3DVertex vertices[4] =
284                         {
285                                 video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1),
286                                 video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1),
287                                 video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0),
288                                 video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
289                         };
290
291                         v3s16 dir = unpackDir(n.param2);
292
293                         for(s32 i=0; i<4; i++)
294                         {
295                                 if(dir == v3s16(1,0,0))
296                                         vertices[i].Pos.rotateXZBy(0);
297                                 if(dir == v3s16(-1,0,0))
298                                         vertices[i].Pos.rotateXZBy(180);
299                                 if(dir == v3s16(0,0,1))
300                                         vertices[i].Pos.rotateXZBy(90);
301                                 if(dir == v3s16(0,0,-1))
302                                         vertices[i].Pos.rotateXZBy(-90);
303                                 if(dir == v3s16(0,-1,0))
304                                         vertices[i].Pos.rotateXYBy(-90);
305                                 if(dir == v3s16(0,1,0))
306                                         vertices[i].Pos.rotateXYBy(90);
307
308                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
309                         }
310
311                         // Set material
312                         video::SMaterial material;
313                         material.setFlag(video::EMF_LIGHTING, false);
314                         material.setFlag(video::EMF_BACK_FACE_CULLING, false);
315                         material.setFlag(video::EMF_BILINEAR_FILTER, false);
316                         material.setFlag(video::EMF_FOG_ENABLE, true);
317                         //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
318                         material.MaterialType
319                                         = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
320
321                         material.setTexture(0, 
322                                         g_texturesource->getTextureRaw("sign_wall.png"));
323
324                         u16 indices[] = {0,1,2,2,3,0};
325                         // Add to mesh collector
326                         collector.append(material, vertices, 4, indices, 6);
327                 }
328                 /*
329                         Add flowing liquid to mesh
330                 */
331                 else if(content_features(n).liquid_type == LIQUID_FLOWING)
332                 {
333                         assert(content_features(n).special_material);
334                         video::SMaterial &liquid_material =
335                                         *content_features(n).special_material;
336                         assert(content_features(n).special_atlas);
337                         AtlasPointer &pa_liquid1 =
338                                         *content_features(n).special_atlas;
339
340                         bool top_is_same_liquid = false;
341                         MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
342                         content_t c_flowing = content_features(n).liquid_alternative_flowing;
343                         content_t c_source = content_features(n).liquid_alternative_source;
344                         if(ntop.getContent() == c_flowing || ntop.getContent() == c_source)
345                                 top_is_same_liquid = true;
346                         
347                         u8 l = 0;
348                         // Use the light of the node on top if possible
349                         if(content_features(ntop).param_type == CPT_LIGHT)
350                                 l = decode_light(ntop.getLightBlend(data->m_daynight_ratio));
351                         // Otherwise use the light of this node (the liquid)
352                         else
353                                 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
354                         video::SColor c = MapBlock_LightColor(
355                                         content_features(n).vertex_alpha, l);
356                         
357                         // Neighbor liquid levels (key = relative position)
358                         // Includes current node
359                         core::map<v3s16, f32> neighbor_levels;
360                         core::map<v3s16, content_t> neighbor_contents;
361                         core::map<v3s16, u8> neighbor_flags;
362                         const u8 neighborflag_top_is_same_liquid = 0x01;
363                         v3s16 neighbor_dirs[9] = {
364                                 v3s16(0,0,0),
365                                 v3s16(0,0,1),
366                                 v3s16(0,0,-1),
367                                 v3s16(1,0,0),
368                                 v3s16(-1,0,0),
369                                 v3s16(1,0,1),
370                                 v3s16(-1,0,-1),
371                                 v3s16(1,0,-1),
372                                 v3s16(-1,0,1),
373                         };
374                         for(u32 i=0; i<9; i++)
375                         {
376                                 content_t content = CONTENT_AIR;
377                                 float level = -0.5 * BS;
378                                 u8 flags = 0;
379                                 // Check neighbor
380                                 v3s16 p2 = p + neighbor_dirs[i];
381                                 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
382                                 if(n2.getContent() != CONTENT_IGNORE)
383                                 {
384                                         content = n2.getContent();
385
386                                         if(n2.getContent() == c_source)
387                                                 level = (-0.5+node_liquid_level) * BS;
388                                         else if(n2.getContent() == c_flowing)
389                                                 level = (-0.5 + ((float)(n2.param2&LIQUID_LEVEL_MASK)
390                                                                 + 0.5) / 8.0 * node_liquid_level) * BS;
391
392                                         // Check node above neighbor.
393                                         // NOTE: This doesn't get executed if neighbor
394                                         //       doesn't exist
395                                         p2.Y += 1;
396                                         n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
397                                         if(n2.getContent() == c_source ||
398                                                         n2.getContent() == c_flowing)
399                                                 flags |= neighborflag_top_is_same_liquid;
400                                 }
401                                 
402                                 neighbor_levels.insert(neighbor_dirs[i], level);
403                                 neighbor_contents.insert(neighbor_dirs[i], content);
404                                 neighbor_flags.insert(neighbor_dirs[i], flags);
405                         }
406
407                         // Corner heights (average between four liquids)
408                         f32 corner_levels[4];
409                         
410                         v3s16 halfdirs[4] = {
411                                 v3s16(0,0,0),
412                                 v3s16(1,0,0),
413                                 v3s16(1,0,1),
414                                 v3s16(0,0,1),
415                         };
416                         for(u32 i=0; i<4; i++)
417                         {
418                                 v3s16 cornerdir = halfdirs[i];
419                                 float cornerlevel = 0;
420                                 u32 valid_count = 0;
421                                 u32 air_count = 0;
422                                 for(u32 j=0; j<4; j++)
423                                 {
424                                         v3s16 neighbordir = cornerdir - halfdirs[j];
425                                         content_t content = neighbor_contents[neighbordir];
426                                         // If top is liquid, draw starting from top of node
427                                         if(neighbor_flags[neighbordir] &
428                                                         neighborflag_top_is_same_liquid)
429                                         {
430                                                 cornerlevel = 0.5*BS;
431                                                 valid_count = 1;
432                                                 break;
433                                         }
434                                         // Source is always the same height
435                                         else if(content == c_source)
436                                         {
437                                                 cornerlevel = (-0.5+node_liquid_level)*BS;
438                                                 valid_count = 1;
439                                                 break;
440                                         }
441                                         // Flowing liquid has level information
442                                         else if(content == c_flowing)
443                                         {
444                                                 cornerlevel += neighbor_levels[neighbordir];
445                                                 valid_count++;
446                                         }
447                                         else if(content == CONTENT_AIR)
448                                         {
449                                                 air_count++;
450                                         }
451                                 }
452                                 if(air_count >= 2)
453                                         cornerlevel = -0.5*BS;
454                                 else if(valid_count > 0)
455                                         cornerlevel /= valid_count;
456                                 corner_levels[i] = cornerlevel;
457                         }
458
459                         /*
460                                 Generate sides
461                         */
462
463                         v3s16 side_dirs[4] = {
464                                 v3s16(1,0,0),
465                                 v3s16(-1,0,0),
466                                 v3s16(0,0,1),
467                                 v3s16(0,0,-1),
468                         };
469                         s16 side_corners[4][2] = {
470                                 {1, 2},
471                                 {3, 0},
472                                 {2, 3},
473                                 {0, 1},
474                         };
475                         for(u32 i=0; i<4; i++)
476                         {
477                                 v3s16 dir = side_dirs[i];
478
479                                 /*
480                                         If our topside is liquid and neighbor's topside
481                                         is liquid, don't draw side face
482                                 */
483                                 if(top_is_same_liquid &&
484                                                 neighbor_flags[dir] & neighborflag_top_is_same_liquid)
485                                         continue;
486
487                                 content_t neighbor_content = neighbor_contents[dir];
488                                 
489                                 // Don't draw face if neighbor is not air or liquid
490                                 if(neighbor_content != CONTENT_AIR
491                                                 && content_liquid(neighbor_content) == false)
492                                         continue;
493                                 
494                                 bool neighbor_is_same_liquid = (neighbor_content == c_source
495                                                 || neighbor_content == c_flowing);
496                                 
497                                 // Don't draw any faces if neighbor same is liquid and top is
498                                 // same liquid
499                                 if(neighbor_is_same_liquid == true
500                                                 && top_is_same_liquid == false)
501                                         continue;
502                                 
503                                 video::S3DVertex vertices[4] =
504                                 {
505                                         /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
506                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
507                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
508                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
509                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
510                                                         pa_liquid1.x0(), pa_liquid1.y1()),
511                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
512                                                         pa_liquid1.x1(), pa_liquid1.y1()),
513                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
514                                                         pa_liquid1.x1(), pa_liquid1.y0()),
515                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
516                                                         pa_liquid1.x0(), pa_liquid1.y0()),
517                                 };
518                                 
519                                 /*
520                                         If our topside is liquid, set upper border of face
521                                         at upper border of node
522                                 */
523                                 if(top_is_same_liquid)
524                                 {
525                                         vertices[2].Pos.Y = 0.5*BS;
526                                         vertices[3].Pos.Y = 0.5*BS;
527                                 }
528                                 /*
529                                         Otherwise upper position of face is corner levels
530                                 */
531                                 else
532                                 {
533                                         vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
534                                         vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
535                                 }
536                                 
537                                 /*
538                                         If neighbor is liquid, lower border of face is corner
539                                         liquid levels
540                                 */
541                                 if(neighbor_is_same_liquid)
542                                 {
543                                         vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
544                                         vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
545                                 }
546                                 /*
547                                         If neighbor is not liquid, lower border of face is
548                                         lower border of node
549                                 */
550                                 else
551                                 {
552                                         vertices[0].Pos.Y = -0.5*BS;
553                                         vertices[1].Pos.Y = -0.5*BS;
554                                 }
555                                 
556                                 for(s32 j=0; j<4; j++)
557                                 {
558                                         if(dir == v3s16(0,0,1))
559                                                 vertices[j].Pos.rotateXZBy(0);
560                                         if(dir == v3s16(0,0,-1))
561                                                 vertices[j].Pos.rotateXZBy(180);
562                                         if(dir == v3s16(-1,0,0))
563                                                 vertices[j].Pos.rotateXZBy(90);
564                                         if(dir == v3s16(1,0,-0))
565                                                 vertices[j].Pos.rotateXZBy(-90);
566                                                 
567                                         // Do this to not cause glitches when two liquids are
568                                         // side-by-side
569                                         if(neighbor_is_same_liquid == false){
570                                                 vertices[j].Pos.X *= 0.98;
571                                                 vertices[j].Pos.Z *= 0.98;
572                                         }
573
574                                         vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
575                                 }
576
577                                 u16 indices[] = {0,1,2,2,3,0};
578                                 // Add to mesh collector
579                                 collector.append(liquid_material, vertices, 4, indices, 6);
580                         }
581                         
582                         /*
583                                 Generate top side, if appropriate
584                         */
585                         
586                         if(top_is_same_liquid == false)
587                         {
588                                 video::S3DVertex vertices[4] =
589                                 {
590                                         /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
591                                         video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
592                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
593                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
594                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
595                                                         pa_liquid1.x0(), pa_liquid1.y1()),
596                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
597                                                         pa_liquid1.x1(), pa_liquid1.y1()),
598                                         video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
599                                                         pa_liquid1.x1(), pa_liquid1.y0()),
600                                         video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
601                                                         pa_liquid1.x0(), pa_liquid1.y0()),
602                                 };
603                                 
604                                 // This fixes a strange bug
605                                 s32 corner_resolve[4] = {3,2,1,0};
606
607                                 for(s32 i=0; i<4; i++)
608                                 {
609                                         //vertices[i].Pos.Y += liquid_level;
610                                         //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
611                                         s32 j = corner_resolve[i];
612                                         vertices[i].Pos.Y += corner_levels[j];
613                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
614                                 }
615
616                                 u16 indices[] = {0,1,2,2,3,0};
617                                 // Add to mesh collector
618                                 collector.append(liquid_material, vertices, 4, indices, 6);
619                         }
620                 }
621                 /*
622                         Add water sources to mesh if using new style
623                 */
624                 else if(content_features(n).liquid_type == LIQUID_SOURCE
625                                 && new_style_water)
626                 {
627                         assert(content_features(n).special_material);
628                         video::SMaterial &liquid_material =
629                                         *content_features(n).special_material;
630                         assert(content_features(n).special_atlas);
631                         AtlasPointer &pa_liquid1 =
632                                         *content_features(n).special_atlas;
633
634                         bool top_is_air = false;
635                         MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
636                         if(n.getContent() == CONTENT_AIR)
637                                 top_is_air = true;
638                         
639                         if(top_is_air == false)
640                                 continue;
641
642                         u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
643                         video::SColor c = MapBlock_LightColor(
644                                         content_features(n).vertex_alpha, l);
645                         
646                         video::S3DVertex vertices[4] =
647                         {
648                                 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
649                                 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
650                                 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
651                                 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
652                                 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
653                                                 pa_liquid1.x0(), pa_liquid1.y1()),
654                                 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
655                                                 pa_liquid1.x1(), pa_liquid1.y1()),
656                                 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
657                                                 pa_liquid1.x1(), pa_liquid1.y0()),
658                                 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
659                                                 pa_liquid1.x0(), pa_liquid1.y0()),
660                         };
661
662                         for(s32 i=0; i<4; i++)
663                         {
664                                 vertices[i].Pos.Y += (-0.5+node_liquid_level)*BS;
665                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
666                         }
667
668                         u16 indices[] = {0,1,2,2,3,0};
669                         // Add to mesh collector
670                         collector.append(liquid_material, vertices, 4, indices, 6);
671                 }
672                 /*
673                         Add leaves if using new style
674                 */
675                 else if(n.getContent() == CONTENT_LEAVES && new_style_leaves)
676                 {
677                         /*u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));*/
678                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
679                         video::SColor c = MapBlock_LightColor(255, l);
680
681                         for(u32 j=0; j<6; j++)
682                         {
683                                 video::S3DVertex vertices[4] =
684                                 {
685                                         /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
686                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
687                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
688                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
689                                         video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
690                                                 pa_leaves1.x0(), pa_leaves1.y1()),
691                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
692                                                 pa_leaves1.x1(), pa_leaves1.y1()),
693                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
694                                                 pa_leaves1.x1(), pa_leaves1.y0()),
695                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
696                                                 pa_leaves1.x0(), pa_leaves1.y0()),
697                                 };
698
699                                 if(j == 0)
700                                 {
701                                         for(u16 i=0; i<4; i++)
702                                                 vertices[i].Pos.rotateXZBy(0);
703                                 }
704                                 else if(j == 1)
705                                 {
706                                         for(u16 i=0; i<4; i++)
707                                                 vertices[i].Pos.rotateXZBy(180);
708                                 }
709                                 else if(j == 2)
710                                 {
711                                         for(u16 i=0; i<4; i++)
712                                                 vertices[i].Pos.rotateXZBy(-90);
713                                 }
714                                 else if(j == 3)
715                                 {
716                                         for(u16 i=0; i<4; i++)
717                                                 vertices[i].Pos.rotateXZBy(90);
718                                 }
719                                 else if(j == 4)
720                                 {
721                                         for(u16 i=0; i<4; i++)
722                                                 vertices[i].Pos.rotateYZBy(-90);
723                                 }
724                                 else if(j == 5)
725                                 {
726                                         for(u16 i=0; i<4; i++)
727                                                 vertices[i].Pos.rotateYZBy(90);
728                                 }
729
730                                 for(u16 i=0; i<4; i++)
731                                 {
732                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
733                                 }
734
735                                 u16 indices[] = {0,1,2,2,3,0};
736                                 // Add to mesh collector
737                                 collector.append(material_leaves1, vertices, 4, indices, 6);
738                         }
739                 }
740                 /*
741                         Add glass
742                 */
743                 else if(n.getContent() == CONTENT_GLASS)
744                 {
745                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
746                         video::SColor c = MapBlock_LightColor(255, l);
747
748                         for(u32 j=0; j<6; j++)
749                         {
750                                 video::S3DVertex vertices[4] =
751                                 {
752                                         video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
753                                                 pa_glass.x0(), pa_glass.y1()),
754                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
755                                                 pa_glass.x1(), pa_glass.y1()),
756                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
757                                                 pa_glass.x1(), pa_glass.y0()),
758                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
759                                                 pa_glass.x0(), pa_glass.y0()),
760                                 };
761
762                                 if(j == 0)
763                                 {
764                                         for(u16 i=0; i<4; i++)
765                                                 vertices[i].Pos.rotateXZBy(0);
766                                 }
767                                 else if(j == 1)
768                                 {
769                                         for(u16 i=0; i<4; i++)
770                                                 vertices[i].Pos.rotateXZBy(180);
771                                 }
772                                 else if(j == 2)
773                                 {
774                                         for(u16 i=0; i<4; i++)
775                                                 vertices[i].Pos.rotateXZBy(-90);
776                                 }
777                                 else if(j == 3)
778                                 {
779                                         for(u16 i=0; i<4; i++)
780                                                 vertices[i].Pos.rotateXZBy(90);
781                                 }
782                                 else if(j == 4)
783                                 {
784                                         for(u16 i=0; i<4; i++)
785                                                 vertices[i].Pos.rotateYZBy(-90);
786                                 }
787                                 else if(j == 5)
788                                 {
789                                         for(u16 i=0; i<4; i++)
790                                                 vertices[i].Pos.rotateYZBy(90);
791                                 }
792
793                                 for(u16 i=0; i<4; i++)
794                                 {
795                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
796                                 }
797
798                                 u16 indices[] = {0,1,2,2,3,0};
799                                 // Add to mesh collector
800                                 collector.append(material_glass, vertices, 4, indices, 6);
801                         }
802                 }
803                 /*
804                         Add fence
805                 */
806                 else if(n.getContent() == CONTENT_FENCE)
807                 {
808                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
809                         video::SColor c = MapBlock_LightColor(255, l);
810
811                         const f32 post_rad=(f32)BS/10;
812                         const f32 bar_rad=(f32)BS/20;
813                         const f32 bar_len=(f32)(BS/2)-post_rad;
814
815                         // The post - always present
816                         v3f pos = intToFloat(p+blockpos_nodes, BS);
817                         f32 postuv[24]={
818                                         0.4,0.4,0.6,0.6,
819                                         0.35,0,0.65,1,
820                                         0.35,0,0.65,1,
821                                         0.35,0,0.65,1,
822                                         0.35,0,0.65,1,
823                                         0.4,0.4,0.6,0.6};
824                         makeCuboid(material_wood, &collector,
825                                 &pa_wood, c, pos,
826                                 post_rad,BS/2,post_rad, 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                         if(n2.getContent() == CONTENT_FENCE)
833                         {
834                                 pos = intToFloat(p+blockpos_nodes, BS);
835                                 pos.X += BS/2;
836                                 pos.Y += BS/4;
837                                 f32 xrailuv[24]={
838                                         0,0.4,1,0.6,
839                                         0,0.4,1,0.6,
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                                 makeCuboid(material_wood, &collector,
845                                         &pa_wood, c, pos,
846                                         bar_len,bar_rad,bar_rad, xrailuv);
847
848                                 pos.Y -= BS/2;
849                                 makeCuboid(material_wood, &collector,
850                                         &pa_wood, c, pos,
851                                         bar_len,bar_rad,bar_rad, 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                         if(n2.getContent() == CONTENT_FENCE)
859                         {
860                                 pos = intToFloat(p+blockpos_nodes, BS);
861                                 pos.Z += BS/2;
862                                 pos.Y += BS/4;
863                                 f32 zrailuv[24]={
864                                         0,0.4,1,0.6,
865                                         0,0.4,1,0.6,
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                                 makeCuboid(material_wood, &collector,
871                                         &pa_wood, c, pos,
872                                         bar_rad,bar_rad,bar_len, zrailuv);
873                                 pos.Y -= BS/2;
874                                 makeCuboid(material_wood, &collector,
875                                         &pa_wood, c, pos,
876                                         bar_rad,bar_rad,bar_len, zrailuv);
877
878                         }
879
880                 }
881 #if 1
882                 /*
883                         Add stones with minerals if stone is invisible
884                 */
885                 else if(n.getContent() == CONTENT_STONE && invisible_stone && n.getMineral() != MINERAL_NONE)
886                 {
887                         for(u32 j=0; j<6; j++)
888                         {
889                                 // NOTE: Hopefully g_6dirs[j] is the right direction...
890                                 v3s16 dir = g_6dirs[j];
891                                 /*u8 l = 0;
892                                 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + dir);
893                                 if(content_features(n2).param_type == CPT_LIGHT)
894                                         l = decode_light(n2.getLightBlend(data->m_daynight_ratio));
895                                 else
896                                         l = 255;*/
897                                 u8 l = 255;
898                                 video::SColor c = MapBlock_LightColor(255, l);
899                                 
900                                 // Get the right texture
901                                 TileSpec ts = n.getTile(dir);
902                                 AtlasPointer ap = ts.texture;
903                                 material_general.setTexture(0, ap.atlas);
904
905                                 video::S3DVertex vertices[4] =
906                                 {
907                                         /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
908                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
909                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
910                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
911                                         video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
912                                                 ap.x0(), ap.y1()),
913                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
914                                                 ap.x1(), ap.y1()),
915                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
916                                                 ap.x1(), ap.y0()),
917                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
918                                                 ap.x0(), ap.y0()),
919                                 };
920
921                                 if(j == 0)
922                                 {
923                                         for(u16 i=0; i<4; i++)
924                                                 vertices[i].Pos.rotateXZBy(0);
925                                 }
926                                 else if(j == 1)
927                                 {
928                                         for(u16 i=0; i<4; i++)
929                                                 vertices[i].Pos.rotateXZBy(180);
930                                 }
931                                 else if(j == 2)
932                                 {
933                                         for(u16 i=0; i<4; i++)
934                                                 vertices[i].Pos.rotateXZBy(-90);
935                                 }
936                                 else if(j == 3)
937                                 {
938                                         for(u16 i=0; i<4; i++)
939                                                 vertices[i].Pos.rotateXZBy(90);
940                                 }
941                                 else if(j == 4)
942
943                                 for(u16 i=0; i<4; i++)
944                                 {
945                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
946                                 }
947
948                                 u16 indices[] = {0,1,2,2,3,0};
949                                 // Add to mesh collector
950                                 collector.append(material_general, vertices, 4, indices, 6);
951                         }
952                 }
953 #endif
954                 else if(n.getContent() == CONTENT_PAPYRUS)
955                 {
956                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
957                         video::SColor c = MapBlock_LightColor(255, l);
958
959                         for(u32 j=0; j<4; j++)
960                         {
961                                 video::S3DVertex vertices[4] =
962                                 {
963                                         video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c,
964                                                 pa_papyrus.x0(), pa_papyrus.y1()),
965                                         video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c,
966                                                 pa_papyrus.x1(), pa_papyrus.y1()),
967                                         video::S3DVertex(BS/2,BS/2,0, 0,0,0, c,
968                                                 pa_papyrus.x1(), pa_papyrus.y0()),
969                                         video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c,
970                                                 pa_papyrus.x0(), pa_papyrus.y0()),
971                                 };
972
973                                 if(j == 0)
974                                 {
975                                         for(u16 i=0; i<4; i++)
976                                                 vertices[i].Pos.rotateXZBy(45);
977                                 }
978                                 else if(j == 1)
979                                 {
980                                         for(u16 i=0; i<4; i++)
981                                                 vertices[i].Pos.rotateXZBy(-45);
982                                 }
983                                 else if(j == 2)
984                                 {
985                                         for(u16 i=0; i<4; i++)
986                                                 vertices[i].Pos.rotateXZBy(135);
987                                 }
988                                 else if(j == 3)
989                                 {
990                                         for(u16 i=0; i<4; i++)
991                                                 vertices[i].Pos.rotateXZBy(-135);
992                                 }
993
994                                 for(u16 i=0; i<4; i++)
995                                 {
996                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
997                                 }
998
999                                 u16 indices[] = {0,1,2,2,3,0};
1000                                 // Add to mesh collector
1001                                 collector.append(material_papyrus, vertices, 4, indices, 6);
1002                         }
1003                 }
1004                 else if(n.getContent() == CONTENT_JUNGLEGRASS)
1005                 {
1006                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1007                         video::SColor c = MapBlock_LightColor(255, l);
1008
1009                         for(u32 j=0; j<4; j++)
1010                         {
1011                                 video::S3DVertex vertices[4] =
1012                                 {
1013                                         video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c,
1014                                                 pa_papyrus.x0(), pa_papyrus.y1()),
1015                                         video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c,
1016                                                 pa_papyrus.x1(), pa_papyrus.y1()),
1017                                         video::S3DVertex(BS/2,BS/1,0, 0,0,0, c,
1018                                                 pa_papyrus.x1(), pa_papyrus.y0()),
1019                                         video::S3DVertex(-BS/2,BS/1,0, 0,0,0, c,
1020                                                 pa_papyrus.x0(), pa_papyrus.y0()),
1021                                 };
1022
1023                                 if(j == 0)
1024                                 {
1025                                         for(u16 i=0; i<4; i++)
1026                                                 vertices[i].Pos.rotateXZBy(45);
1027                                 }
1028                                 else if(j == 1)
1029                                 {
1030                                         for(u16 i=0; i<4; i++)
1031                                                 vertices[i].Pos.rotateXZBy(-45);
1032                                 }
1033                                 else if(j == 2)
1034                                 {
1035                                         for(u16 i=0; i<4; i++)
1036                                                 vertices[i].Pos.rotateXZBy(135);
1037                                 }
1038                                 else if(j == 3)
1039                                 {
1040                                         for(u16 i=0; i<4; i++)
1041                                                 vertices[i].Pos.rotateXZBy(-135);
1042                                 }
1043
1044                                 for(u16 i=0; i<4; i++)
1045                                 {
1046                                         vertices[i].Pos *= 1.3;
1047                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1048                                 }
1049
1050                                 u16 indices[] = {0,1,2,2,3,0};
1051                                 // Add to mesh collector
1052                                 collector.append(material_junglegrass, vertices, 4, indices, 6);
1053                         }
1054                 }
1055                 else if(n.getContent() == CONTENT_RAIL)
1056                 {
1057                         u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1058                         video::SColor c = MapBlock_LightColor(255, l);
1059
1060                         bool is_rail_x [] = { false, false };  /* x-1, x+1 */
1061                         bool is_rail_z [] = { false, false };  /* z-1, z+1 */
1062
1063                         MapNode n_minus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1,y,z));
1064                         MapNode n_plus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1,y,z));
1065                         MapNode n_minus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z-1));
1066                         MapNode n_plus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z+1));
1067
1068                         if(n_minus_x.getContent() == CONTENT_RAIL)
1069                                 is_rail_x[0] = true;
1070                         if(n_plus_x.getContent() == CONTENT_RAIL)
1071                                 is_rail_x[1] = true;
1072                         if(n_minus_z.getContent() == CONTENT_RAIL)
1073                                 is_rail_z[0] = true;
1074                         if(n_plus_z.getContent() == CONTENT_RAIL)
1075                                 is_rail_z[1] = true;
1076
1077                         float d = (float)BS/16;
1078                         video::S3DVertex vertices[4] =
1079                         {
1080                                 video::S3DVertex(-BS/2,-BS/2+d,-BS/2, 0,0,0, c,
1081                                         0, 1),
1082                                 video::S3DVertex(BS/2,-BS/2+d,-BS/2, 0,0,0, c,
1083                                         1, 1),
1084                                 video::S3DVertex(BS/2,-BS/2+d,BS/2, 0,0,0, c,
1085                                         1, 0),
1086                                 video::S3DVertex(-BS/2,-BS/2+d,BS/2, 0,0,0, c,
1087                                         0, 0),
1088                         };
1089
1090                         video::SMaterial material_rail;
1091                         material_rail.setFlag(video::EMF_LIGHTING, false);
1092                         material_rail.setFlag(video::EMF_BACK_FACE_CULLING, false);
1093                         material_rail.setFlag(video::EMF_BILINEAR_FILTER, false);
1094                         material_rail.setFlag(video::EMF_FOG_ENABLE, true);
1095                         material_rail.MaterialType
1096                                         = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1097
1098                         int adjacencies = is_rail_x[0] + is_rail_x[1] + is_rail_z[0] + is_rail_z[1];
1099
1100                         // Assign textures
1101                         if(adjacencies < 2)
1102                                 material_rail.setTexture(0, g_texturesource->getTextureRaw("rail.png"));
1103                         else if(adjacencies == 2)
1104                         {
1105                                 if((is_rail_x[0] && is_rail_x[1]) || (is_rail_z[0] && is_rail_z[1]))
1106                                         material_rail.setTexture(0, g_texturesource->getTextureRaw("rail.png"));
1107                                 else
1108                                         material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_curved.png"));
1109                         }
1110                         else if(adjacencies == 3)
1111                                 material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_t_junction.png"));
1112                         else if(adjacencies == 4)
1113                                 material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_crossing.png"));
1114
1115                         // Rotate textures
1116                         int angle = 0;
1117
1118                         if(adjacencies == 1)
1119                         {
1120                                 if(is_rail_x[0] || is_rail_x[1])
1121                                         angle = 90;
1122                         }
1123                         else if(adjacencies == 2)
1124                         {
1125                                 if(is_rail_x[0] && is_rail_x[1])
1126                                         angle = 90;
1127                                 else if(is_rail_x[0] && is_rail_z[0])
1128                                         angle = 270;
1129                                 else if(is_rail_x[0] && is_rail_z[1])
1130                                         angle = 180;
1131                                 else if(is_rail_x[1] && is_rail_z[1])
1132                                         angle = 90;
1133                         }
1134                         else if(adjacencies == 3)
1135                         {
1136                                 if(!is_rail_x[0])
1137                                         angle=0;
1138                                 if(!is_rail_x[1])
1139                                         angle=180;
1140                                 if(!is_rail_z[0])
1141                                         angle=90;
1142                                 if(!is_rail_z[1])
1143                                         angle=270;
1144                         }
1145
1146                         if(angle != 0) {
1147                                 for(u16 i=0; i<4; i++)
1148                                         vertices[i].Pos.rotateXZBy(angle);
1149                         }
1150
1151                         for(s32 i=0; i<4; i++)
1152                         {
1153                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1154                         }
1155
1156                         u16 indices[] = {0,1,2,2,3,0};
1157                         collector.append(material_rail, vertices, 4, indices, 6);
1158                 }
1159                 else if (n.getContent() == CONTENT_LADDER) {
1160                         u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1161                         video::SColor c(255,l,l,l);
1162
1163                         float d = (float)BS/16;
1164
1165                         // Assume wall is at X+
1166                         video::S3DVertex vertices[4] =
1167                         {
1168                                 video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1),
1169                                 video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1),
1170                                 video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0),
1171                                 video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
1172                         };
1173
1174                         v3s16 dir = unpackDir(n.param2);
1175
1176                         for(s32 i=0; i<4; i++)
1177                         {
1178                                 if(dir == v3s16(1,0,0))
1179                                         vertices[i].Pos.rotateXZBy(0);
1180                                 if(dir == v3s16(-1,0,0))
1181                                         vertices[i].Pos.rotateXZBy(180);
1182                                 if(dir == v3s16(0,0,1))
1183                                         vertices[i].Pos.rotateXZBy(90);
1184                                 if(dir == v3s16(0,0,-1))
1185                                         vertices[i].Pos.rotateXZBy(-90);
1186                                 if(dir == v3s16(0,-1,0))
1187                                         vertices[i].Pos.rotateXYBy(-90);
1188                                 if(dir == v3s16(0,1,0))
1189                                         vertices[i].Pos.rotateXYBy(90);
1190
1191                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1192                         }
1193
1194                         video::SMaterial material_ladder;
1195                         material_ladder.setFlag(video::EMF_LIGHTING, false);
1196                         material_ladder.setFlag(video::EMF_BACK_FACE_CULLING, false);
1197                         material_ladder.setFlag(video::EMF_BILINEAR_FILTER, false);
1198                         material_ladder.setFlag(video::EMF_FOG_ENABLE, true);
1199                         material_ladder.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1200                         material_ladder.setTexture(0, g_texturesource->getTextureRaw("ladder.png"));
1201
1202                         u16 indices[] = {0,1,2,2,3,0};
1203                         // Add to mesh collector
1204                         collector.append(material_ladder, vertices, 4, indices, 6);
1205                 }
1206         }
1207 }
1208 #endif
1209