]> git.lizzy.rs Git - minetest.git/blob - src/content_mapblock.cpp
Fixed liquid mesh generation
[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                                 u8 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                                         u8 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                                         /*// Air is liquid level 0
452                                         else if(content == CONTENT_AIR)
453                                         {
454                                                 cornerlevel += -0.5*BS;
455                                                 valid_count++;
456                                         }*/
457                                 }
458                                 if(air_count >= 2)
459                                         cornerlevel = -0.5*BS;
460                                 else if(valid_count > 0)
461                                         cornerlevel /= valid_count;
462                                 corner_levels[i] = cornerlevel;
463                         }
464
465                         /*
466                                 Generate sides
467                         */
468
469                         v3s16 side_dirs[4] = {
470                                 v3s16(1,0,0),
471                                 v3s16(-1,0,0),
472                                 v3s16(0,0,1),
473                                 v3s16(0,0,-1),
474                         };
475                         s16 side_corners[4][2] = {
476                                 {1, 2},
477                                 {3, 0},
478                                 {2, 3},
479                                 {0, 1},
480                         };
481                         for(u32 i=0; i<4; i++)
482                         {
483                                 v3s16 dir = side_dirs[i];
484
485                                 /*
486                                         If our topside is liquid and neighbor's topside
487                                         is liquid, don't draw side face
488                                 */
489                                 if(top_is_same_liquid &&
490                                                 neighbor_flags[dir] & neighborflag_top_is_same_liquid)
491                                         continue;
492
493                                 u8 neighbor_content = neighbor_contents[dir];
494                                 
495                                 // Don't draw face if neighbor is not air or liquid
496                                 if(neighbor_content != CONTENT_AIR
497                                                 && neighbor_content != c_source)
498                                         continue;
499                                 
500                                 bool neighbor_is_liquid = (neighbor_content == c_source);
501                                 
502                                 // Don't draw any faces if neighbor is liquid and top is liquid
503                                 if(neighbor_is_liquid == true && top_is_same_liquid == false)
504                                         continue;
505                                 
506                                 video::S3DVertex vertices[4] =
507                                 {
508                                         /*video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
509                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
510                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
511                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
512                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
513                                                         pa_liquid1.x0(), pa_liquid1.y1()),
514                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
515                                                         pa_liquid1.x1(), pa_liquid1.y1()),
516                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
517                                                         pa_liquid1.x1(), pa_liquid1.y0()),
518                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
519                                                         pa_liquid1.x0(), pa_liquid1.y0()),
520                                 };
521                                 
522                                 /*
523                                         If our topside is liquid, set upper border of face
524                                         at upper border of node
525                                 */
526                                 if(top_is_same_liquid)
527                                 {
528                                         vertices[2].Pos.Y = 0.5*BS;
529                                         vertices[3].Pos.Y = 0.5*BS;
530                                 }
531                                 /*
532                                         Otherwise upper position of face is corner levels
533                                 */
534                                 else
535                                 {
536                                         vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
537                                         vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
538                                 }
539                                 
540                                 /*
541                                         If neighbor is liquid, lower border of face is corner
542                                         liquid levels
543                                 */
544                                 if(neighbor_is_liquid)
545                                 {
546                                         vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
547                                         vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
548                                 }
549                                 /*
550                                         If neighbor is not liquid, lower border of face is
551                                         lower border of node
552                                 */
553                                 else
554                                 {
555                                         vertices[0].Pos.Y = -0.5*BS;
556                                         vertices[1].Pos.Y = -0.5*BS;
557                                 }
558                                 
559                                 for(s32 j=0; j<4; j++)
560                                 {
561                                         if(dir == v3s16(0,0,1))
562                                                 vertices[j].Pos.rotateXZBy(0);
563                                         if(dir == v3s16(0,0,-1))
564                                                 vertices[j].Pos.rotateXZBy(180);
565                                         if(dir == v3s16(-1,0,0))
566                                                 vertices[j].Pos.rotateXZBy(90);
567                                         if(dir == v3s16(1,0,-0))
568                                                 vertices[j].Pos.rotateXZBy(-90);
569
570                                         vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
571                                 }
572
573                                 u16 indices[] = {0,1,2,2,3,0};
574                                 // Add to mesh collector
575                                 collector.append(liquid_material, vertices, 4, indices, 6);
576                         }
577                         
578                         /*
579                                 Generate top side, if appropriate
580                         */
581                         
582                         if(top_is_same_liquid == false)
583                         {
584                                 video::S3DVertex vertices[4] =
585                                 {
586                                         /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
587                                         video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
588                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
589                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
590                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
591                                                         pa_liquid1.x0(), pa_liquid1.y1()),
592                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
593                                                         pa_liquid1.x1(), pa_liquid1.y1()),
594                                         video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
595                                                         pa_liquid1.x1(), pa_liquid1.y0()),
596                                         video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
597                                                         pa_liquid1.x0(), pa_liquid1.y0()),
598                                 };
599                                 
600                                 // This fixes a strange bug
601                                 s32 corner_resolve[4] = {3,2,1,0};
602
603                                 for(s32 i=0; i<4; i++)
604                                 {
605                                         //vertices[i].Pos.Y += liquid_level;
606                                         //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
607                                         s32 j = corner_resolve[i];
608                                         vertices[i].Pos.Y += corner_levels[j];
609                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
610                                 }
611
612                                 u16 indices[] = {0,1,2,2,3,0};
613                                 // Add to mesh collector
614                                 collector.append(liquid_material, vertices, 4, indices, 6);
615                         }
616                 }
617                 /*
618                         Add water sources to mesh if using new style
619                 */
620                 else if(content_features(n).liquid_type == LIQUID_SOURCE
621                                 && new_style_water)
622                 {
623                         assert(content_features(n).special_material);
624                         video::SMaterial &liquid_material =
625                                         *content_features(n).special_material;
626                         assert(content_features(n).special_atlas);
627                         AtlasPointer &pa_liquid1 =
628                                         *content_features(n).special_atlas;
629
630                         bool top_is_air = false;
631                         MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
632                         if(n.getContent() == CONTENT_AIR)
633                                 top_is_air = true;
634                         
635                         if(top_is_air == false)
636                                 continue;
637
638                         u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
639                         video::SColor c = MapBlock_LightColor(
640                                         content_features(n).vertex_alpha, l);
641                         
642                         video::S3DVertex vertices[4] =
643                         {
644                                 /*video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
645                                 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
646                                 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
647                                 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),*/
648                                 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
649                                                 pa_liquid1.x0(), pa_liquid1.y1()),
650                                 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
651                                                 pa_liquid1.x1(), pa_liquid1.y1()),
652                                 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
653                                                 pa_liquid1.x1(), pa_liquid1.y0()),
654                                 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
655                                                 pa_liquid1.x0(), pa_liquid1.y0()),
656                         };
657
658                         for(s32 i=0; i<4; i++)
659                         {
660                                 vertices[i].Pos.Y += (-0.5+node_liquid_level)*BS;
661                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
662                         }
663
664                         u16 indices[] = {0,1,2,2,3,0};
665                         // Add to mesh collector
666                         collector.append(liquid_material, vertices, 4, indices, 6);
667                 }
668                 /*
669                         Add leaves if using new style
670                 */
671                 else if(n.getContent() == CONTENT_LEAVES && new_style_leaves)
672                 {
673                         /*u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));*/
674                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
675                         video::SColor c = MapBlock_LightColor(255, l);
676
677                         for(u32 j=0; j<6; j++)
678                         {
679                                 video::S3DVertex vertices[4] =
680                                 {
681                                         /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
682                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
683                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
684                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
685                                         video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
686                                                 pa_leaves1.x0(), pa_leaves1.y1()),
687                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
688                                                 pa_leaves1.x1(), pa_leaves1.y1()),
689                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
690                                                 pa_leaves1.x1(), pa_leaves1.y0()),
691                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
692                                                 pa_leaves1.x0(), pa_leaves1.y0()),
693                                 };
694
695                                 if(j == 0)
696                                 {
697                                         for(u16 i=0; i<4; i++)
698                                                 vertices[i].Pos.rotateXZBy(0);
699                                 }
700                                 else if(j == 1)
701                                 {
702                                         for(u16 i=0; i<4; i++)
703                                                 vertices[i].Pos.rotateXZBy(180);
704                                 }
705                                 else if(j == 2)
706                                 {
707                                         for(u16 i=0; i<4; i++)
708                                                 vertices[i].Pos.rotateXZBy(-90);
709                                 }
710                                 else if(j == 3)
711                                 {
712                                         for(u16 i=0; i<4; i++)
713                                                 vertices[i].Pos.rotateXZBy(90);
714                                 }
715                                 else if(j == 4)
716                                 {
717                                         for(u16 i=0; i<4; i++)
718                                                 vertices[i].Pos.rotateYZBy(-90);
719                                 }
720                                 else if(j == 5)
721                                 {
722                                         for(u16 i=0; i<4; i++)
723                                                 vertices[i].Pos.rotateYZBy(90);
724                                 }
725
726                                 for(u16 i=0; i<4; i++)
727                                 {
728                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
729                                 }
730
731                                 u16 indices[] = {0,1,2,2,3,0};
732                                 // Add to mesh collector
733                                 collector.append(material_leaves1, vertices, 4, indices, 6);
734                         }
735                 }
736                 /*
737                         Add glass
738                 */
739                 else if(n.getContent() == CONTENT_GLASS)
740                 {
741                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
742                         video::SColor c = MapBlock_LightColor(255, l);
743
744                         for(u32 j=0; j<6; j++)
745                         {
746                                 video::S3DVertex vertices[4] =
747                                 {
748                                         video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
749                                                 pa_glass.x0(), pa_glass.y1()),
750                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
751                                                 pa_glass.x1(), pa_glass.y1()),
752                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
753                                                 pa_glass.x1(), pa_glass.y0()),
754                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
755                                                 pa_glass.x0(), pa_glass.y0()),
756                                 };
757
758                                 if(j == 0)
759                                 {
760                                         for(u16 i=0; i<4; i++)
761                                                 vertices[i].Pos.rotateXZBy(0);
762                                 }
763                                 else if(j == 1)
764                                 {
765                                         for(u16 i=0; i<4; i++)
766                                                 vertices[i].Pos.rotateXZBy(180);
767                                 }
768                                 else if(j == 2)
769                                 {
770                                         for(u16 i=0; i<4; i++)
771                                                 vertices[i].Pos.rotateXZBy(-90);
772                                 }
773                                 else if(j == 3)
774                                 {
775                                         for(u16 i=0; i<4; i++)
776                                                 vertices[i].Pos.rotateXZBy(90);
777                                 }
778                                 else if(j == 4)
779                                 {
780                                         for(u16 i=0; i<4; i++)
781                                                 vertices[i].Pos.rotateYZBy(-90);
782                                 }
783                                 else if(j == 5)
784                                 {
785                                         for(u16 i=0; i<4; i++)
786                                                 vertices[i].Pos.rotateYZBy(90);
787                                 }
788
789                                 for(u16 i=0; i<4; i++)
790                                 {
791                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
792                                 }
793
794                                 u16 indices[] = {0,1,2,2,3,0};
795                                 // Add to mesh collector
796                                 collector.append(material_glass, vertices, 4, indices, 6);
797                         }
798                 }
799                 /*
800                         Add fence
801                 */
802                 else if(n.getContent() == CONTENT_FENCE)
803                 {
804                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
805                         video::SColor c = MapBlock_LightColor(255, l);
806
807                         const f32 post_rad=(f32)BS/10;
808                         const f32 bar_rad=(f32)BS/20;
809                         const f32 bar_len=(f32)(BS/2)-post_rad;
810
811                         // The post - always present
812                         v3f pos = intToFloat(p+blockpos_nodes, BS);
813                         f32 postuv[24]={
814                                         0.4,0.4,0.6,0.6,
815                                         0.35,0,0.65,1,
816                                         0.35,0,0.65,1,
817                                         0.35,0,0.65,1,
818                                         0.35,0,0.65,1,
819                                         0.4,0.4,0.6,0.6};
820                         makeCuboid(material_wood, &collector,
821                                 &pa_wood, c, pos,
822                                 post_rad,BS/2,post_rad, postuv);
823
824                         // Now a section of fence, +X, if there's a post there
825                         v3s16 p2 = p;
826                         p2.X++;
827                         MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
828                         if(n2.getContent() == CONTENT_FENCE)
829                         {
830                                 pos = intToFloat(p+blockpos_nodes, BS);
831                                 pos.X += BS/2;
832                                 pos.Y += BS/4;
833                                 f32 xrailuv[24]={
834                                         0,0.4,1,0.6,
835                                         0,0.4,1,0.6,
836                                         0,0.4,1,0.6,
837                                         0,0.4,1,0.6,
838                                         0,0.4,1,0.6,
839                                         0,0.4,1,0.6};
840                                 makeCuboid(material_wood, &collector,
841                                         &pa_wood, c, pos,
842                                         bar_len,bar_rad,bar_rad, xrailuv);
843
844                                 pos.Y -= BS/2;
845                                 makeCuboid(material_wood, &collector,
846                                         &pa_wood, c, pos,
847                                         bar_len,bar_rad,bar_rad, xrailuv);
848                         }
849
850                         // Now a section of fence, +Z, if there's a post there
851                         p2 = p;
852                         p2.Z++;
853                         n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
854                         if(n2.getContent() == CONTENT_FENCE)
855                         {
856                                 pos = intToFloat(p+blockpos_nodes, BS);
857                                 pos.Z += BS/2;
858                                 pos.Y += BS/4;
859                                 f32 zrailuv[24]={
860                                         0,0.4,1,0.6,
861                                         0,0.4,1,0.6,
862                                         0,0.4,1,0.6,
863                                         0,0.4,1,0.6,
864                                         0,0.4,1,0.6,
865                                         0,0.4,1,0.6};
866                                 makeCuboid(material_wood, &collector,
867                                         &pa_wood, c, pos,
868                                         bar_rad,bar_rad,bar_len, zrailuv);
869                                 pos.Y -= BS/2;
870                                 makeCuboid(material_wood, &collector,
871                                         &pa_wood, c, pos,
872                                         bar_rad,bar_rad,bar_len, zrailuv);
873
874                         }
875
876                 }
877 #if 1
878                 /*
879                         Add stones with minerals if stone is invisible
880                 */
881                 else if(n.getContent() == CONTENT_STONE && invisible_stone && n.getMineral() != MINERAL_NONE)
882                 {
883                         for(u32 j=0; j<6; j++)
884                         {
885                                 // NOTE: Hopefully g_6dirs[j] is the right direction...
886                                 v3s16 dir = g_6dirs[j];
887                                 /*u8 l = 0;
888                                 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + dir);
889                                 if(content_features(n2).param_type == CPT_LIGHT)
890                                         l = decode_light(n2.getLightBlend(data->m_daynight_ratio));
891                                 else
892                                         l = 255;*/
893                                 u8 l = 255;
894                                 video::SColor c = MapBlock_LightColor(255, l);
895                                 
896                                 // Get the right texture
897                                 TileSpec ts = n.getTile(dir);
898                                 AtlasPointer ap = ts.texture;
899                                 material_general.setTexture(0, ap.atlas);
900
901                                 video::S3DVertex vertices[4] =
902                                 {
903                                         /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
904                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
905                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
906                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
907                                         video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
908                                                 ap.x0(), ap.y1()),
909                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
910                                                 ap.x1(), ap.y1()),
911                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
912                                                 ap.x1(), ap.y0()),
913                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
914                                                 ap.x0(), ap.y0()),
915                                 };
916
917                                 if(j == 0)
918                                 {
919                                         for(u16 i=0; i<4; i++)
920                                                 vertices[i].Pos.rotateXZBy(0);
921                                 }
922                                 else if(j == 1)
923                                 {
924                                         for(u16 i=0; i<4; i++)
925                                                 vertices[i].Pos.rotateXZBy(180);
926                                 }
927                                 else if(j == 2)
928                                 {
929                                         for(u16 i=0; i<4; i++)
930                                                 vertices[i].Pos.rotateXZBy(-90);
931                                 }
932                                 else if(j == 3)
933                                 {
934                                         for(u16 i=0; i<4; i++)
935                                                 vertices[i].Pos.rotateXZBy(90);
936                                 }
937                                 else if(j == 4)
938
939                                 for(u16 i=0; i<4; i++)
940                                 {
941                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
942                                 }
943
944                                 u16 indices[] = {0,1,2,2,3,0};
945                                 // Add to mesh collector
946                                 collector.append(material_general, vertices, 4, indices, 6);
947                         }
948                 }
949 #endif
950                 else if(n.getContent() == CONTENT_PAPYRUS)
951                 {
952                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
953                         video::SColor c = MapBlock_LightColor(255, l);
954
955                         for(u32 j=0; j<4; j++)
956                         {
957                                 video::S3DVertex vertices[4] =
958                                 {
959                                         video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c,
960                                                 pa_papyrus.x0(), pa_papyrus.y1()),
961                                         video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c,
962                                                 pa_papyrus.x1(), pa_papyrus.y1()),
963                                         video::S3DVertex(BS/2,BS/2,0, 0,0,0, c,
964                                                 pa_papyrus.x1(), pa_papyrus.y0()),
965                                         video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c,
966                                                 pa_papyrus.x0(), pa_papyrus.y0()),
967                                 };
968
969                                 if(j == 0)
970                                 {
971                                         for(u16 i=0; i<4; i++)
972                                                 vertices[i].Pos.rotateXZBy(45);
973                                 }
974                                 else if(j == 1)
975                                 {
976                                         for(u16 i=0; i<4; i++)
977                                                 vertices[i].Pos.rotateXZBy(-45);
978                                 }
979                                 else if(j == 2)
980                                 {
981                                         for(u16 i=0; i<4; i++)
982                                                 vertices[i].Pos.rotateXZBy(135);
983                                 }
984                                 else if(j == 3)
985                                 {
986                                         for(u16 i=0; i<4; i++)
987                                                 vertices[i].Pos.rotateXZBy(-135);
988                                 }
989
990                                 for(u16 i=0; i<4; i++)
991                                 {
992                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
993                                 }
994
995                                 u16 indices[] = {0,1,2,2,3,0};
996                                 // Add to mesh collector
997                                 collector.append(material_papyrus, vertices, 4, indices, 6);
998                         }
999                 }
1000                 else if(n.getContent() == CONTENT_JUNGLEGRASS)
1001                 {
1002                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1003                         video::SColor c = MapBlock_LightColor(255, l);
1004
1005                         for(u32 j=0; j<4; j++)
1006                         {
1007                                 video::S3DVertex vertices[4] =
1008                                 {
1009                                         video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c,
1010                                                 pa_papyrus.x0(), pa_papyrus.y1()),
1011                                         video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c,
1012                                                 pa_papyrus.x1(), pa_papyrus.y1()),
1013                                         video::S3DVertex(BS/2,BS/1,0, 0,0,0, c,
1014                                                 pa_papyrus.x1(), pa_papyrus.y0()),
1015                                         video::S3DVertex(-BS/2,BS/1,0, 0,0,0, c,
1016                                                 pa_papyrus.x0(), pa_papyrus.y0()),
1017                                 };
1018
1019                                 if(j == 0)
1020                                 {
1021                                         for(u16 i=0; i<4; i++)
1022                                                 vertices[i].Pos.rotateXZBy(45);
1023                                 }
1024                                 else if(j == 1)
1025                                 {
1026                                         for(u16 i=0; i<4; i++)
1027                                                 vertices[i].Pos.rotateXZBy(-45);
1028                                 }
1029                                 else if(j == 2)
1030                                 {
1031                                         for(u16 i=0; i<4; i++)
1032                                                 vertices[i].Pos.rotateXZBy(135);
1033                                 }
1034                                 else if(j == 3)
1035                                 {
1036                                         for(u16 i=0; i<4; i++)
1037                                                 vertices[i].Pos.rotateXZBy(-135);
1038                                 }
1039
1040                                 for(u16 i=0; i<4; i++)
1041                                 {
1042                                         vertices[i].Pos *= 1.3;
1043                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1044                                 }
1045
1046                                 u16 indices[] = {0,1,2,2,3,0};
1047                                 // Add to mesh collector
1048                                 collector.append(material_junglegrass, vertices, 4, indices, 6);
1049                         }
1050                 }
1051                 else if(n.getContent() == CONTENT_RAIL)
1052                 {
1053                         u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1054                         video::SColor c = MapBlock_LightColor(255, l);
1055
1056                         bool is_rail_x [] = { false, false };  /* x-1, x+1 */
1057                         bool is_rail_z [] = { false, false };  /* z-1, z+1 */
1058
1059                         MapNode n_minus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1,y,z));
1060                         MapNode n_plus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1,y,z));
1061                         MapNode n_minus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z-1));
1062                         MapNode n_plus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z+1));
1063
1064                         if(n_minus_x.getContent() == CONTENT_RAIL)
1065                                 is_rail_x[0] = true;
1066                         if(n_plus_x.getContent() == CONTENT_RAIL)
1067                                 is_rail_x[1] = true;
1068                         if(n_minus_z.getContent() == CONTENT_RAIL)
1069                                 is_rail_z[0] = true;
1070                         if(n_plus_z.getContent() == CONTENT_RAIL)
1071                                 is_rail_z[1] = true;
1072
1073                         float d = (float)BS/16;
1074                         video::S3DVertex vertices[4] =
1075                         {
1076                                 video::S3DVertex(-BS/2,-BS/2+d,-BS/2, 0,0,0, c,
1077                                         0, 1),
1078                                 video::S3DVertex(BS/2,-BS/2+d,-BS/2, 0,0,0, c,
1079                                         1, 1),
1080                                 video::S3DVertex(BS/2,-BS/2+d,BS/2, 0,0,0, c,
1081                                         1, 0),
1082                                 video::S3DVertex(-BS/2,-BS/2+d,BS/2, 0,0,0, c,
1083                                         0, 0),
1084                         };
1085
1086                         video::SMaterial material_rail;
1087                         material_rail.setFlag(video::EMF_LIGHTING, false);
1088                         material_rail.setFlag(video::EMF_BACK_FACE_CULLING, false);
1089                         material_rail.setFlag(video::EMF_BILINEAR_FILTER, false);
1090                         material_rail.setFlag(video::EMF_FOG_ENABLE, true);
1091                         material_rail.MaterialType
1092                                         = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1093
1094                         int adjacencies = is_rail_x[0] + is_rail_x[1] + is_rail_z[0] + is_rail_z[1];
1095
1096                         // Assign textures
1097                         if(adjacencies < 2)
1098                                 material_rail.setTexture(0, g_texturesource->getTextureRaw("rail.png"));
1099                         else if(adjacencies == 2)
1100                         {
1101                                 if((is_rail_x[0] && is_rail_x[1]) || (is_rail_z[0] && is_rail_z[1]))
1102                                         material_rail.setTexture(0, g_texturesource->getTextureRaw("rail.png"));
1103                                 else
1104                                         material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_curved.png"));
1105                         }
1106                         else if(adjacencies == 3)
1107                                 material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_t_junction.png"));
1108                         else if(adjacencies == 4)
1109                                 material_rail.setTexture(0, g_texturesource->getTextureRaw("rail_crossing.png"));
1110
1111                         // Rotate textures
1112                         int angle = 0;
1113
1114                         if(adjacencies == 1)
1115                         {
1116                                 if(is_rail_x[0] || is_rail_x[1])
1117                                         angle = 90;
1118                         }
1119                         else if(adjacencies == 2)
1120                         {
1121                                 if(is_rail_x[0] && is_rail_x[1])
1122                                         angle = 90;
1123                                 else if(is_rail_x[0] && is_rail_z[0])
1124                                         angle = 270;
1125                                 else if(is_rail_x[0] && is_rail_z[1])
1126                                         angle = 180;
1127                                 else if(is_rail_x[1] && is_rail_z[1])
1128                                         angle = 90;
1129                         }
1130                         else if(adjacencies == 3)
1131                         {
1132                                 if(!is_rail_x[0])
1133                                         angle=0;
1134                                 if(!is_rail_x[1])
1135                                         angle=180;
1136                                 if(!is_rail_z[0])
1137                                         angle=90;
1138                                 if(!is_rail_z[1])
1139                                         angle=270;
1140                         }
1141
1142                         if(angle != 0) {
1143                                 for(u16 i=0; i<4; i++)
1144                                         vertices[i].Pos.rotateXZBy(angle);
1145                         }
1146
1147                         for(s32 i=0; i<4; i++)
1148                         {
1149                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1150                         }
1151
1152                         u16 indices[] = {0,1,2,2,3,0};
1153                         collector.append(material_rail, vertices, 4, indices, 6);
1154                 }
1155                 else if (n.getContent() == CONTENT_LADDER) {
1156                         u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1157                         video::SColor c(255,l,l,l);
1158
1159                         float d = (float)BS/16;
1160
1161                         // Assume wall is at X+
1162                         video::S3DVertex vertices[4] =
1163                         {
1164                                 video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c, 0,1),
1165                                 video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c, 1,1),
1166                                 video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c, 1,0),
1167                                 video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c, 0,0),
1168                         };
1169
1170                         v3s16 dir = unpackDir(n.param2);
1171
1172                         for(s32 i=0; i<4; i++)
1173                         {
1174                                 if(dir == v3s16(1,0,0))
1175                                         vertices[i].Pos.rotateXZBy(0);
1176                                 if(dir == v3s16(-1,0,0))
1177                                         vertices[i].Pos.rotateXZBy(180);
1178                                 if(dir == v3s16(0,0,1))
1179                                         vertices[i].Pos.rotateXZBy(90);
1180                                 if(dir == v3s16(0,0,-1))
1181                                         vertices[i].Pos.rotateXZBy(-90);
1182                                 if(dir == v3s16(0,-1,0))
1183                                         vertices[i].Pos.rotateXYBy(-90);
1184                                 if(dir == v3s16(0,1,0))
1185                                         vertices[i].Pos.rotateXYBy(90);
1186
1187                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1188                         }
1189
1190                         video::SMaterial material_ladder;
1191                         material_ladder.setFlag(video::EMF_LIGHTING, false);
1192                         material_ladder.setFlag(video::EMF_BACK_FACE_CULLING, false);
1193                         material_ladder.setFlag(video::EMF_BILINEAR_FILTER, false);
1194                         material_ladder.setFlag(video::EMF_FOG_ENABLE, true);
1195                         material_ladder.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1196                         material_ladder.setTexture(0, g_texturesource->getTextureRaw("ladder.png"));
1197
1198                         u16 indices[] = {0,1,2,2,3,0};
1199                         // Add to mesh collector
1200                         collector.append(material_ladder, vertices, 4, indices, 6);
1201                 }
1202         }
1203 }
1204 #endif
1205