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