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