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