ad4601d062921fe0ebf4226b5f62175da0dfd99b
[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, true);
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, true);
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                         assert(content_features(n).special_atlas);
368                         AtlasPointer &pa_liquid1 =
369                                         *content_features(n).special_atlas;
370
371                         bool top_is_same_liquid = false;
372                         MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
373                         content_t c_flowing = content_features(n).liquid_alternative_flowing;
374                         content_t c_source = content_features(n).liquid_alternative_source;
375                         if(ntop.getContent() == c_flowing || ntop.getContent() == c_source)
376                                 top_is_same_liquid = true;
377                         
378                         u8 l = 0;
379                         // Use the light of the node on top if possible
380                         if(content_features(ntop).param_type == CPT_LIGHT)
381                                 l = decode_light(ntop.getLightBlend(data->m_daynight_ratio));
382                         // Otherwise use the light of this node (the liquid)
383                         else
384                                 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
385                         video::SColor c = MapBlock_LightColor(
386                                         content_features(n).vertex_alpha, l);
387                         
388                         // Neighbor liquid levels (key = relative position)
389                         // Includes current node
390                         core::map<v3s16, f32> neighbor_levels;
391                         core::map<v3s16, content_t> neighbor_contents;
392                         core::map<v3s16, u8> neighbor_flags;
393                         const u8 neighborflag_top_is_same_liquid = 0x01;
394                         v3s16 neighbor_dirs[9] = {
395                                 v3s16(0,0,0),
396                                 v3s16(0,0,1),
397                                 v3s16(0,0,-1),
398                                 v3s16(1,0,0),
399                                 v3s16(-1,0,0),
400                                 v3s16(1,0,1),
401                                 v3s16(-1,0,-1),
402                                 v3s16(1,0,-1),
403                                 v3s16(-1,0,1),
404                         };
405                         for(u32 i=0; i<9; i++)
406                         {
407                                 content_t content = CONTENT_AIR;
408                                 float level = -0.5 * BS;
409                                 u8 flags = 0;
410                                 // Check neighbor
411                                 v3s16 p2 = p + neighbor_dirs[i];
412                                 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
413                                 if(n2.getContent() != CONTENT_IGNORE)
414                                 {
415                                         content = n2.getContent();
416
417                                         if(n2.getContent() == c_source)
418                                                 level = (-0.5+node_liquid_level) * BS;
419                                         else if(n2.getContent() == c_flowing)
420                                                 level = (-0.5 + ((float)(n2.param2&LIQUID_LEVEL_MASK)
421                                                                 + 0.5) / 8.0 * node_liquid_level) * BS;
422
423                                         // Check node above neighbor.
424                                         // NOTE: This doesn't get executed if neighbor
425                                         //       doesn't exist
426                                         p2.Y += 1;
427                                         n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
428                                         if(n2.getContent() == c_source ||
429                                                         n2.getContent() == c_flowing)
430                                                 flags |= neighborflag_top_is_same_liquid;
431                                 }
432                                 
433                                 neighbor_levels.insert(neighbor_dirs[i], level);
434                                 neighbor_contents.insert(neighbor_dirs[i], content);
435                                 neighbor_flags.insert(neighbor_dirs[i], flags);
436                         }
437
438                         // Corner heights (average between four liquids)
439                         f32 corner_levels[4];
440                         
441                         v3s16 halfdirs[4] = {
442                                 v3s16(0,0,0),
443                                 v3s16(1,0,0),
444                                 v3s16(1,0,1),
445                                 v3s16(0,0,1),
446                         };
447                         for(u32 i=0; i<4; i++)
448                         {
449                                 v3s16 cornerdir = halfdirs[i];
450                                 float cornerlevel = 0;
451                                 u32 valid_count = 0;
452                                 u32 air_count = 0;
453                                 for(u32 j=0; j<4; j++)
454                                 {
455                                         v3s16 neighbordir = cornerdir - halfdirs[j];
456                                         content_t content = neighbor_contents[neighbordir];
457                                         // If top is liquid, draw starting from top of node
458                                         if(neighbor_flags[neighbordir] &
459                                                         neighborflag_top_is_same_liquid)
460                                         {
461                                                 cornerlevel = 0.5*BS;
462                                                 valid_count = 1;
463                                                 break;
464                                         }
465                                         // Source is always the same height
466                                         else if(content == c_source)
467                                         {
468                                                 cornerlevel = (-0.5+node_liquid_level)*BS;
469                                                 valid_count = 1;
470                                                 break;
471                                         }
472                                         // Flowing liquid has level information
473                                         else if(content == c_flowing)
474                                         {
475                                                 cornerlevel += neighbor_levels[neighbordir];
476                                                 valid_count++;
477                                         }
478                                         else if(content == CONTENT_AIR)
479                                         {
480                                                 air_count++;
481                                         }
482                                 }
483                                 if(air_count >= 2)
484                                         cornerlevel = -0.5*BS;
485                                 else if(valid_count > 0)
486                                         cornerlevel /= valid_count;
487                                 corner_levels[i] = cornerlevel;
488                         }
489
490                         /*
491                                 Generate sides
492                         */
493
494                         v3s16 side_dirs[4] = {
495                                 v3s16(1,0,0),
496                                 v3s16(-1,0,0),
497                                 v3s16(0,0,1),
498                                 v3s16(0,0,-1),
499                         };
500                         s16 side_corners[4][2] = {
501                                 {1, 2},
502                                 {3, 0},
503                                 {2, 3},
504                                 {0, 1},
505                         };
506                         for(u32 i=0; i<4; i++)
507                         {
508                                 v3s16 dir = side_dirs[i];
509
510                                 /*
511                                         If our topside is liquid and neighbor's topside
512                                         is liquid, don't draw side face
513                                 */
514                                 if(top_is_same_liquid &&
515                                                 neighbor_flags[dir] & neighborflag_top_is_same_liquid)
516                                         continue;
517
518                                 content_t neighbor_content = neighbor_contents[dir];
519                                 
520                                 // Don't draw face if neighbor is not air or liquid
521                                 if(neighbor_content != CONTENT_AIR
522                                                 && content_liquid(neighbor_content) == false)
523                                         continue;
524                                 
525                                 bool neighbor_is_same_liquid = (neighbor_content == c_source
526                                                 || neighbor_content == c_flowing);
527                                 
528                                 // Don't draw any faces if neighbor same is liquid and top is
529                                 // same liquid
530                                 if(neighbor_is_same_liquid == true
531                                                 && top_is_same_liquid == false)
532                                         continue;
533                                 
534                                 video::S3DVertex vertices[4] =
535                                 {
536                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
537                                                         pa_liquid1.x0(), pa_liquid1.y1()),
538                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
539                                                         pa_liquid1.x1(), pa_liquid1.y1()),
540                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
541                                                         pa_liquid1.x1(), pa_liquid1.y0()),
542                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
543                                                         pa_liquid1.x0(), pa_liquid1.y0()),
544                                 };
545                                 
546                                 /*
547                                         If our topside is liquid, set upper border of face
548                                         at upper border of node
549                                 */
550                                 if(top_is_same_liquid)
551                                 {
552                                         vertices[2].Pos.Y = 0.5*BS;
553                                         vertices[3].Pos.Y = 0.5*BS;
554                                 }
555                                 /*
556                                         Otherwise upper position of face is corner levels
557                                 */
558                                 else
559                                 {
560                                         vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
561                                         vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
562                                 }
563                                 
564                                 /*
565                                         If neighbor is liquid, lower border of face is corner
566                                         liquid levels
567                                 */
568                                 if(neighbor_is_same_liquid)
569                                 {
570                                         vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
571                                         vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
572                                 }
573                                 /*
574                                         If neighbor is not liquid, lower border of face is
575                                         lower border of node
576                                 */
577                                 else
578                                 {
579                                         vertices[0].Pos.Y = -0.5*BS;
580                                         vertices[1].Pos.Y = -0.5*BS;
581                                 }
582                                 
583                                 for(s32 j=0; j<4; j++)
584                                 {
585                                         if(dir == v3s16(0,0,1))
586                                                 vertices[j].Pos.rotateXZBy(0);
587                                         if(dir == v3s16(0,0,-1))
588                                                 vertices[j].Pos.rotateXZBy(180);
589                                         if(dir == v3s16(-1,0,0))
590                                                 vertices[j].Pos.rotateXZBy(90);
591                                         if(dir == v3s16(1,0,-0))
592                                                 vertices[j].Pos.rotateXZBy(-90);
593                                                 
594                                         // Do this to not cause glitches when two liquids are
595                                         // side-by-side
596                                         if(neighbor_is_same_liquid == false){
597                                                 vertices[j].Pos.X *= 0.98;
598                                                 vertices[j].Pos.Z *= 0.98;
599                                         }
600
601                                         vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
602                                 }
603
604                                 u16 indices[] = {0,1,2,2,3,0};
605                                 // Add to mesh collector
606                                 collector.append(liquid_material, vertices, 4, indices, 6);
607                         }
608                         
609                         /*
610                                 Generate top side, if appropriate
611                         */
612                         
613                         if(top_is_same_liquid == false)
614                         {
615                                 video::S3DVertex vertices[4] =
616                                 {
617                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
618                                                         pa_liquid1.x0(), pa_liquid1.y1()),
619                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
620                                                         pa_liquid1.x1(), pa_liquid1.y1()),
621                                         video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
622                                                         pa_liquid1.x1(), pa_liquid1.y0()),
623                                         video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
624                                                         pa_liquid1.x0(), pa_liquid1.y0()),
625                                 };
626                                 
627                                 // This fixes a strange bug
628                                 s32 corner_resolve[4] = {3,2,1,0};
629
630                                 for(s32 i=0; i<4; i++)
631                                 {
632                                         //vertices[i].Pos.Y += liquid_level;
633                                         //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
634                                         s32 j = corner_resolve[i];
635                                         vertices[i].Pos.Y += corner_levels[j];
636                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
637                                 }
638
639                                 u16 indices[] = {0,1,2,2,3,0};
640                                 // Add to mesh collector
641                                 collector.append(liquid_material, vertices, 4, indices, 6);
642                         }
643                 }
644                 /*
645                         Add water sources to mesh if using new style
646                 */
647                 else if(content_features(n).liquid_type == LIQUID_SOURCE
648                                 && new_style_water)
649                 {
650                         assert(content_features(n).special_material);
651                         video::SMaterial &liquid_material =
652                                         *content_features(n).special_material;
653                         assert(content_features(n).special_atlas);
654                         AtlasPointer &pa_liquid1 =
655                                         *content_features(n).special_atlas;
656
657                         bool top_is_air = false;
658                         MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
659                         if(n.getContent() == CONTENT_AIR)
660                                 top_is_air = true;
661                         
662                         if(top_is_air == false)
663                                 continue;
664
665                         u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
666                         video::SColor c = MapBlock_LightColor(
667                                         content_features(n).vertex_alpha, l);
668                         
669                         video::S3DVertex vertices[4] =
670                         {
671                                 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
672                                                 pa_liquid1.x0(), pa_liquid1.y1()),
673                                 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
674                                                 pa_liquid1.x1(), pa_liquid1.y1()),
675                                 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
676                                                 pa_liquid1.x1(), pa_liquid1.y0()),
677                                 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
678                                                 pa_liquid1.x0(), pa_liquid1.y0()),
679                         };
680
681                         for(s32 i=0; i<4; i++)
682                         {
683                                 vertices[i].Pos.Y += (-0.5+node_liquid_level)*BS;
684                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
685                         }
686
687                         u16 indices[] = {0,1,2,2,3,0};
688                         // Add to mesh collector
689                         collector.append(liquid_material, vertices, 4, indices, 6);
690                 }
691                 /*
692                         Add leaves if using new style
693                 */
694                 else if(n.getContent() == CONTENT_LEAVES && new_style_leaves)
695                 {
696                         /*u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));*/
697                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
698                         video::SColor c = MapBlock_LightColor(255, l);
699
700                         for(u32 j=0; j<6; j++)
701                         {
702                                 video::S3DVertex vertices[4] =
703                                 {
704                                         video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
705                                                 pa_leaves1.x0(), pa_leaves1.y1()),
706                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
707                                                 pa_leaves1.x1(), pa_leaves1.y1()),
708                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
709                                                 pa_leaves1.x1(), pa_leaves1.y0()),
710                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
711                                                 pa_leaves1.x0(), pa_leaves1.y0()),
712                                 };
713
714                                 if(j == 0)
715                                 {
716                                         for(u16 i=0; i<4; i++)
717                                                 vertices[i].Pos.rotateXZBy(0);
718                                 }
719                                 else if(j == 1)
720                                 {
721                                         for(u16 i=0; i<4; i++)
722                                                 vertices[i].Pos.rotateXZBy(180);
723                                 }
724                                 else if(j == 2)
725                                 {
726                                         for(u16 i=0; i<4; i++)
727                                                 vertices[i].Pos.rotateXZBy(-90);
728                                 }
729                                 else if(j == 3)
730                                 {
731                                         for(u16 i=0; i<4; i++)
732                                                 vertices[i].Pos.rotateXZBy(90);
733                                 }
734                                 else if(j == 4)
735                                 {
736                                         for(u16 i=0; i<4; i++)
737                                                 vertices[i].Pos.rotateYZBy(-90);
738                                 }
739                                 else if(j == 5)
740                                 {
741                                         for(u16 i=0; i<4; i++)
742                                                 vertices[i].Pos.rotateYZBy(90);
743                                 }
744
745                                 for(u16 i=0; i<4; i++)
746                                 {
747                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
748                                 }
749
750                                 u16 indices[] = {0,1,2,2,3,0};
751                                 // Add to mesh collector
752                                 collector.append(material_leaves1, vertices, 4, indices, 6);
753                         }
754                 }
755                 /*
756                         Add glass
757                 */
758                 else if(n.getContent() == CONTENT_GLASS)
759                 {
760                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
761                         video::SColor c = MapBlock_LightColor(255, l);
762
763                         for(u32 j=0; j<6; j++)
764                         {
765                                 video::S3DVertex vertices[4] =
766                                 {
767                                         video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
768                                                 pa_glass.x0(), pa_glass.y1()),
769                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
770                                                 pa_glass.x1(), pa_glass.y1()),
771                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
772                                                 pa_glass.x1(), pa_glass.y0()),
773                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
774                                                 pa_glass.x0(), pa_glass.y0()),
775                                 };
776
777                                 if(j == 0)
778                                 {
779                                         for(u16 i=0; i<4; i++)
780                                                 vertices[i].Pos.rotateXZBy(0);
781                                 }
782                                 else if(j == 1)
783                                 {
784                                         for(u16 i=0; i<4; i++)
785                                                 vertices[i].Pos.rotateXZBy(180);
786                                 }
787                                 else if(j == 2)
788                                 {
789                                         for(u16 i=0; i<4; i++)
790                                                 vertices[i].Pos.rotateXZBy(-90);
791                                 }
792                                 else if(j == 3)
793                                 {
794                                         for(u16 i=0; i<4; i++)
795                                                 vertices[i].Pos.rotateXZBy(90);
796                                 }
797                                 else if(j == 4)
798                                 {
799                                         for(u16 i=0; i<4; i++)
800                                                 vertices[i].Pos.rotateYZBy(-90);
801                                 }
802                                 else if(j == 5)
803                                 {
804                                         for(u16 i=0; i<4; i++)
805                                                 vertices[i].Pos.rotateYZBy(90);
806                                 }
807
808                                 for(u16 i=0; i<4; i++)
809                                 {
810                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
811                                 }
812
813                                 u16 indices[] = {0,1,2,2,3,0};
814                                 // Add to mesh collector
815                                 collector.append(material_glass, vertices, 4, indices, 6);
816                         }
817                 }
818                 /*
819                         Add fence
820                 */
821                 else if(n.getContent() == CONTENT_FENCE)
822                 {
823                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
824                         video::SColor c = MapBlock_LightColor(255, l);
825
826                         const f32 post_rad=(f32)BS/10;
827                         const f32 bar_rad=(f32)BS/20;
828                         const f32 bar_len=(f32)(BS/2)-post_rad;
829
830                         // The post - always present
831                         v3f pos = intToFloat(p+blockpos_nodes, BS);
832                         f32 postuv[24]={
833                                         0.4,0.4,0.6,0.6,
834                                         0.35,0,0.65,1,
835                                         0.35,0,0.65,1,
836                                         0.35,0,0.65,1,
837                                         0.35,0,0.65,1,
838                                         0.4,0.4,0.6,0.6};
839                         makeCuboid(material_wood, &collector,
840                                 &pa_wood, c, pos,
841                                 post_rad,BS/2,post_rad, postuv);
842
843                         // Now a section of fence, +X, if there's a post there
844                         v3s16 p2 = p;
845                         p2.X++;
846                         MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
847                         if(n2.getContent() == CONTENT_FENCE)
848                         {
849                                 pos = intToFloat(p+blockpos_nodes, BS);
850                                 pos.X += BS/2;
851                                 pos.Y += BS/4;
852                                 f32 xrailuv[24]={
853                                         0,0.4,1,0.6,
854                                         0,0.4,1,0.6,
855                                         0,0.4,1,0.6,
856                                         0,0.4,1,0.6,
857                                         0,0.4,1,0.6,
858                                         0,0.4,1,0.6};
859                                 makeCuboid(material_wood, &collector,
860                                         &pa_wood, c, pos,
861                                         bar_len,bar_rad,bar_rad, xrailuv);
862
863                                 pos.Y -= BS/2;
864                                 makeCuboid(material_wood, &collector,
865                                         &pa_wood, c, pos,
866                                         bar_len,bar_rad,bar_rad, xrailuv);
867                         }
868
869                         // Now a section of fence, +Z, if there's a post there
870                         p2 = p;
871                         p2.Z++;
872                         n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
873                         if(n2.getContent() == CONTENT_FENCE)
874                         {
875                                 pos = intToFloat(p+blockpos_nodes, BS);
876                                 pos.Z += BS/2;
877                                 pos.Y += BS/4;
878                                 f32 zrailuv[24]={
879                                         0,0.4,1,0.6,
880                                         0,0.4,1,0.6,
881                                         0,0.4,1,0.6,
882                                         0,0.4,1,0.6,
883                                         0,0.4,1,0.6,
884                                         0,0.4,1,0.6};
885                                 makeCuboid(material_wood, &collector,
886                                         &pa_wood, c, pos,
887                                         bar_rad,bar_rad,bar_len, zrailuv);
888                                 pos.Y -= BS/2;
889                                 makeCuboid(material_wood, &collector,
890                                         &pa_wood, c, pos,
891                                         bar_rad,bar_rad,bar_len, zrailuv);
892
893                         }
894
895                 }
896 #if 1
897                 /*
898                         Add stones with minerals if stone is invisible
899                 */
900                 else if(n.getContent() == CONTENT_STONE && invisible_stone && n.getMineral() != MINERAL_NONE)
901                 {
902                         for(u32 j=0; j<6; j++)
903                         {
904                                 // NOTE: Hopefully g_6dirs[j] is the right direction...
905                                 v3s16 dir = g_6dirs[j];
906                                 /*u8 l = 0;
907                                 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + dir);
908                                 if(content_features(n2).param_type == CPT_LIGHT)
909                                         l = decode_light(n2.getLightBlend(data->m_daynight_ratio));
910                                 else
911                                         l = 255;*/
912                                 u8 l = 255;
913                                 video::SColor c = MapBlock_LightColor(255, l);
914                                 
915                                 // Get the right texture
916                                 TileSpec ts = n.getTile(dir);
917                                 AtlasPointer ap = ts.texture;
918                                 material_general.setTexture(0, ap.atlas);
919
920                                 video::S3DVertex vertices[4] =
921                                 {
922                                         video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
923                                                 ap.x0(), ap.y1()),
924                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
925                                                 ap.x1(), ap.y1()),
926                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
927                                                 ap.x1(), ap.y0()),
928                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
929                                                 ap.x0(), ap.y0()),
930                                 };
931
932                                 if(j == 0)
933                                 {
934                                         for(u16 i=0; i<4; i++)
935                                                 vertices[i].Pos.rotateXZBy(0);
936                                 }
937                                 else if(j == 1)
938                                 {
939                                         for(u16 i=0; i<4; i++)
940                                                 vertices[i].Pos.rotateXZBy(180);
941                                 }
942                                 else if(j == 2)
943                                 {
944                                         for(u16 i=0; i<4; i++)
945                                                 vertices[i].Pos.rotateXZBy(-90);
946                                 }
947                                 else if(j == 3)
948                                 {
949                                         for(u16 i=0; i<4; i++)
950                                                 vertices[i].Pos.rotateXZBy(90);
951                                 }
952                                 else if(j == 4)
953
954                                 for(u16 i=0; i<4; i++)
955                                 {
956                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
957                                 }
958
959                                 u16 indices[] = {0,1,2,2,3,0};
960                                 // Add to mesh collector
961                                 collector.append(material_general, vertices, 4, indices, 6);
962                         }
963                 }
964 #endif
965                 else if(n.getContent() == CONTENT_PAPYRUS)
966                 {
967                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
968                         video::SColor c = MapBlock_LightColor(255, l);
969
970                         for(u32 j=0; j<4; j++)
971                         {
972                                 video::S3DVertex vertices[4] =
973                                 {
974                                         video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c,
975                                                 pa_papyrus.x0(), pa_papyrus.y1()),
976                                         video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c,
977                                                 pa_papyrus.x1(), pa_papyrus.y1()),
978                                         video::S3DVertex(BS/2,BS/2,0, 0,0,0, c,
979                                                 pa_papyrus.x1(), pa_papyrus.y0()),
980                                         video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c,
981                                                 pa_papyrus.x0(), pa_papyrus.y0()),
982                                 };
983
984                                 if(j == 0)
985                                 {
986                                         for(u16 i=0; i<4; i++)
987                                                 vertices[i].Pos.rotateXZBy(45);
988                                 }
989                                 else if(j == 1)
990                                 {
991                                         for(u16 i=0; i<4; i++)
992                                                 vertices[i].Pos.rotateXZBy(-45);
993                                 }
994                                 else if(j == 2)
995                                 {
996                                         for(u16 i=0; i<4; i++)
997                                                 vertices[i].Pos.rotateXZBy(135);
998                                 }
999                                 else if(j == 3)
1000                                 {
1001                                         for(u16 i=0; i<4; i++)
1002                                                 vertices[i].Pos.rotateXZBy(-135);
1003                                 }
1004
1005                                 for(u16 i=0; i<4; i++)
1006                                 {
1007                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1008                                 }
1009
1010                                 u16 indices[] = {0,1,2,2,3,0};
1011                                 // Add to mesh collector
1012                                 collector.append(material_papyrus, vertices, 4, indices, 6);
1013                         }
1014                 }
1015                 else if(n.getContent() == CONTENT_JUNGLEGRASS)
1016                 {
1017                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1018                         video::SColor c = MapBlock_LightColor(255, l);
1019
1020                         for(u32 j=0; j<4; j++)
1021                         {
1022                                 video::S3DVertex vertices[4] =
1023                                 {
1024                                         video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c,
1025                                                 pa_junglegrass.x0(), pa_junglegrass.y1()),
1026                                         video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c,
1027                                                 pa_junglegrass.x1(), pa_junglegrass.y1()),
1028                                         video::S3DVertex(BS/2,BS/1,0, 0,0,0, c,
1029                                                 pa_junglegrass.x1(), pa_junglegrass.y0()),
1030                                         video::S3DVertex(-BS/2,BS/1,0, 0,0,0, c,
1031                                                 pa_junglegrass.x0(), pa_junglegrass.y0()),
1032                                 };
1033
1034                                 if(j == 0)
1035                                 {
1036                                         for(u16 i=0; i<4; i++)
1037                                                 vertices[i].Pos.rotateXZBy(45);
1038                                 }
1039                                 else if(j == 1)
1040                                 {
1041                                         for(u16 i=0; i<4; i++)
1042                                                 vertices[i].Pos.rotateXZBy(-45);
1043                                 }
1044                                 else if(j == 2)
1045                                 {
1046                                         for(u16 i=0; i<4; i++)
1047                                                 vertices[i].Pos.rotateXZBy(135);
1048                                 }
1049                                 else if(j == 3)
1050                                 {
1051                                         for(u16 i=0; i<4; i++)
1052                                                 vertices[i].Pos.rotateXZBy(-135);
1053                                 }
1054
1055                                 for(u16 i=0; i<4; i++)
1056                                 {
1057                                         vertices[i].Pos *= 1.3;
1058                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1059                                 }
1060
1061                                 u16 indices[] = {0,1,2,2,3,0};
1062                                 // Add to mesh collector
1063                                 collector.append(material_junglegrass, vertices, 4, indices, 6);
1064                         }
1065                 }
1066                 else if(n.getContent() == CONTENT_RAIL)
1067                 {
1068                         bool is_rail_x [] = { false, false };  /* x-1, x+1 */
1069                         bool is_rail_z [] = { false, false };  /* z-1, z+1 */
1070
1071                         MapNode n_minus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1,y,z));
1072                         MapNode n_plus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1,y,z));
1073                         MapNode n_minus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z-1));
1074                         MapNode n_plus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z+1));
1075
1076                         if(n_minus_x.getContent() == CONTENT_RAIL)
1077                                 is_rail_x[0] = true;
1078                         if(n_plus_x.getContent() == CONTENT_RAIL)
1079                                 is_rail_x[1] = true;
1080                         if(n_minus_z.getContent() == CONTENT_RAIL)
1081                                 is_rail_z[0] = true;
1082                         if(n_plus_z.getContent() == CONTENT_RAIL)
1083                                 is_rail_z[1] = true;
1084
1085                         int adjacencies = is_rail_x[0] + is_rail_x[1] + is_rail_z[0] + is_rail_z[1];
1086
1087                         // Assign textures
1088                         const char *texturename = "rail.png";
1089                         if(adjacencies < 2)
1090                                 texturename = "rail.png";
1091                         else if(adjacencies == 2)
1092                         {
1093                                 if((is_rail_x[0] && is_rail_x[1]) || (is_rail_z[0] && is_rail_z[1]))
1094                                         texturename = "rail.png";
1095                                 else
1096                                         texturename = "rail_curved.png";
1097                         }
1098                         else if(adjacencies == 3)
1099                                 texturename = "rail_t_junction.png";
1100                         else if(adjacencies == 4)
1101                                 texturename = "rail_crossing.png";
1102                         
1103                         AtlasPointer ap = g_texturesource->getTexture(texturename);
1104
1105                         video::SMaterial material_rail;
1106                         material_rail.setFlag(video::EMF_LIGHTING, false);
1107                         material_rail.setFlag(video::EMF_BACK_FACE_CULLING, true);
1108                         material_rail.setFlag(video::EMF_BILINEAR_FILTER, false);
1109                         material_rail.setFlag(video::EMF_FOG_ENABLE, true);
1110                         material_rail.MaterialType
1111                                         = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1112                         material_rail.setTexture(0, ap.atlas);
1113
1114                         u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1115                         video::SColor c = MapBlock_LightColor(255, l);
1116
1117                         float d = (float)BS/16;
1118                         video::S3DVertex vertices[4] =
1119                         {
1120                                 video::S3DVertex(-BS/2,-BS/2+d,-BS/2, 0,0,0, c,
1121                                                 ap.x0(), ap.y1()),
1122                                 video::S3DVertex(BS/2,-BS/2+d,-BS/2, 0,0,0, c,
1123                                                 ap.x1(), ap.y1()),
1124                                 video::S3DVertex(BS/2,-BS/2+d,BS/2, 0,0,0, c,
1125                                                 ap.x1(), ap.y0()),
1126                                 video::S3DVertex(-BS/2,-BS/2+d,BS/2, 0,0,0, c,
1127                                                 ap.x0(), ap.y0()),
1128                         };
1129
1130                         // Rotate textures
1131                         int angle = 0;
1132
1133                         if(adjacencies == 1)
1134                         {
1135                                 if(is_rail_x[0] || is_rail_x[1])
1136                                         angle = 90;
1137                         }
1138                         else if(adjacencies == 2)
1139                         {
1140                                 if(is_rail_x[0] && is_rail_x[1])
1141                                         angle = 90;
1142                                 else if(is_rail_x[0] && is_rail_z[0])
1143                                         angle = 270;
1144                                 else if(is_rail_x[0] && is_rail_z[1])
1145                                         angle = 180;
1146                                 else if(is_rail_x[1] && is_rail_z[1])
1147                                         angle = 90;
1148                         }
1149                         else if(adjacencies == 3)
1150                         {
1151                                 if(!is_rail_x[0])
1152                                         angle=0;
1153                                 if(!is_rail_x[1])
1154                                         angle=180;
1155                                 if(!is_rail_z[0])
1156                                         angle=90;
1157                                 if(!is_rail_z[1])
1158                                         angle=270;
1159                         }
1160
1161                         if(angle != 0) {
1162                                 for(u16 i=0; i<4; i++)
1163                                         vertices[i].Pos.rotateXZBy(angle);
1164                         }
1165
1166                         for(s32 i=0; i<4; i++)
1167                         {
1168                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1169                         }
1170
1171                         u16 indices[] = {0,1,2,2,3,0};
1172                         collector.append(material_rail, vertices, 4, indices, 6);
1173                 }
1174                 else if (n.getContent() == CONTENT_LADDER) {
1175                         AtlasPointer ap = g_texturesource->getTexture("ladder.png");
1176                         
1177                         // Set material
1178                         video::SMaterial material_ladder;
1179                         material_ladder.setFlag(video::EMF_LIGHTING, false);
1180                         material_ladder.setFlag(video::EMF_BACK_FACE_CULLING, true);
1181                         material_ladder.setFlag(video::EMF_BILINEAR_FILTER, false);
1182                         material_ladder.setFlag(video::EMF_FOG_ENABLE, true);
1183                         material_ladder.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
1184                         material_ladder.setTexture(0, ap.atlas);
1185
1186                         u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio));
1187                         video::SColor c(255,l,l,l);
1188
1189                         float d = (float)BS/16;
1190
1191                         // Assume wall is at X+
1192                         video::S3DVertex vertices[4] =
1193                         {
1194                                 video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c,
1195                                                 ap.x0(), ap.y1()),
1196                                 video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c,
1197                                                 ap.x1(), ap.y1()),
1198                                 video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c,
1199                                                 ap.x1(), ap.y0()),
1200                                 video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c,
1201                                                 ap.x0(), ap.y0()),
1202                         };
1203
1204                         v3s16 dir = unpackDir(n.param2);
1205
1206                         for(s32 i=0; i<4; i++)
1207                         {
1208                                 if(dir == v3s16(1,0,0))
1209                                         vertices[i].Pos.rotateXZBy(0);
1210                                 if(dir == v3s16(-1,0,0))
1211                                         vertices[i].Pos.rotateXZBy(180);
1212                                 if(dir == v3s16(0,0,1))
1213                                         vertices[i].Pos.rotateXZBy(90);
1214                                 if(dir == v3s16(0,0,-1))
1215                                         vertices[i].Pos.rotateXZBy(-90);
1216                                 if(dir == v3s16(0,-1,0))
1217                                         vertices[i].Pos.rotateXYBy(-90);
1218                                 if(dir == v3s16(0,1,0))
1219                                         vertices[i].Pos.rotateXYBy(90);
1220
1221                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1222                         }
1223
1224                         u16 indices[] = {0,1,2,2,3,0};
1225                         // Add to mesh collector
1226                         collector.append(material_ladder, vertices, 4, indices, 6);
1227                 }
1228                 else if(n.getContent() == CONTENT_APPLE)
1229                 {
1230                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1231                         video::SColor c = MapBlock_LightColor(255, l);
1232
1233                         for(u32 j=0; j<4; j++)
1234                         {
1235                                 video::S3DVertex vertices[4] =
1236                                 {
1237                                         video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c,
1238                                                 pa_apple.x0(), pa_apple.y1()),
1239                                         video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c,
1240                                                 pa_apple.x1(), pa_apple.y1()),
1241                                         video::S3DVertex(BS/2,BS/2,0, 0,0,0, c,
1242                                                 pa_apple.x1(), pa_apple.y0()),
1243                                         video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c,
1244                                                 pa_apple.x0(), pa_apple.y0()),
1245                                 };
1246
1247                                 if(j == 0)
1248                                 {
1249                                         for(u16 i=0; i<4; i++)
1250                                                 vertices[i].Pos.rotateXZBy(45);
1251                                 }
1252                                 else if(j == 1)
1253                                 {
1254                                         for(u16 i=0; i<4; i++)
1255                                                 vertices[i].Pos.rotateXZBy(-45);
1256                                 }
1257                                 else if(j == 2)
1258                                 {
1259                                         for(u16 i=0; i<4; i++)
1260                                                 vertices[i].Pos.rotateXZBy(135);
1261                                 }
1262                                 else if(j == 3)
1263                                 {
1264                                         for(u16 i=0; i<4; i++)
1265                                                 vertices[i].Pos.rotateXZBy(-135);
1266                                 }
1267
1268                                 for(u16 i=0; i<4; i++)
1269                                 {
1270                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1271                                 }
1272
1273                                 u16 indices[] = {0,1,2,2,3,0};
1274                                 // Add to mesh collector
1275                                 collector.append(material_apple, vertices, 4, indices, 6);
1276                         }
1277                 }
1278                 else if(n.getContent() == CONTENT_SAPLING) {
1279                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio)));
1280                         video::SColor c = MapBlock_LightColor(255, l);
1281
1282                         for(u32 j=0; j<4; j++)
1283                         {
1284                                 video::S3DVertex vertices[4] =
1285                                 {
1286                                         video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c,
1287                                                 pa_sapling.x0(), pa_sapling.y1()),
1288                                         video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c,
1289                                                 pa_sapling.x1(), pa_sapling.y1()),
1290                                         video::S3DVertex(BS/2,BS/1,0, 0,0,0, c,
1291                                                 pa_sapling.x1(), pa_sapling.y0()),
1292                                         video::S3DVertex(-BS/2,BS/1,0, 0,0,0, c,
1293                                                 pa_sapling.x0(), pa_sapling.y0()),
1294                                 };
1295
1296                                 if(j == 0)
1297                                 {
1298                                         for(u16 i=0; i<4; i++)
1299                                                 vertices[i].Pos.rotateXZBy(45);
1300                                 }
1301                                 else if(j == 1)
1302                                 {
1303                                         for(u16 i=0; i<4; i++)
1304                                                 vertices[i].Pos.rotateXZBy(-45);
1305                                 }
1306                                 else if(j == 2)
1307                                 {
1308                                         for(u16 i=0; i<4; i++)
1309                                                 vertices[i].Pos.rotateXZBy(135);
1310                                 }
1311                                 else if(j == 3)
1312                                 {
1313                                         for(u16 i=0; i<4; i++)
1314                                                 vertices[i].Pos.rotateXZBy(-135);
1315                                 }
1316
1317                                 for(u16 i=0; i<4; i++)
1318                                 {
1319                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1320                                 }
1321
1322                                 u16 indices[] = {0,1,2,2,3,0};
1323                                 // Add to mesh collector
1324                                 collector.append(material_sapling, vertices, 4, indices, 6);
1325                         }
1326                 }
1327         }
1328 }
1329 #endif
1330