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