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