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