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