]> git.lizzy.rs Git - minetest.git/blob - src/content_mapblock.cpp
e8e4fd231680a486136449e07646538dcb8a4144
[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
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         INodeDefManager *nodedef = gamedef->ndef();
129
130         // 0ms
131         //TimeTaker timer("mapblock_mesh_generate_special()");
132
133         /*
134                 Some settings
135         */
136         bool new_style_water = g_settings->getBool("new_style_water");
137         
138         float node_liquid_level = 1.0;
139         if(new_style_water)
140                 node_liquid_level = 0.85;
141         
142         v3s16 blockpos_nodes = data->m_blockpos*MAP_BLOCKSIZE;
143
144         /*// General ground material for special output
145         // Texture is modified just before usage
146         video::SMaterial material_general;
147         material_general.setFlag(video::EMF_LIGHTING, false);
148         material_general.setFlag(video::EMF_BILINEAR_FILTER, false);
149         material_general.setFlag(video::EMF_FOG_ENABLE, true);
150         material_general.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;*/
151
152         for(s16 z=0; z<MAP_BLOCKSIZE; z++)
153         for(s16 y=0; y<MAP_BLOCKSIZE; y++)
154         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
155         {
156                 v3s16 p(x,y,z);
157
158                 MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes+p);
159                 const ContentFeatures &f = nodedef->get(n);
160
161                 // Only solidness=0 stuff is drawn here
162                 if(f.solidness != 0)
163                         continue;
164                 
165                 switch(f.drawtype){
166                 default:
167                         infostream<<"Got "<<f.drawtype<<std::endl;
168                         assert(0);
169                         break;
170                 case NDT_AIRLIKE:
171                         break;
172                 case NDT_LIQUID:
173                 {
174                         /*
175                                 Add water sources to mesh if using new style
176                         */
177                         assert(nodedef->get(n).special_materials[0]);
178                         //assert(nodedef->get(n).special_materials[1]);
179                         assert(nodedef->get(n).special_aps[0]);
180
181                         video::SMaterial &liquid_material =
182                                         *nodedef->get(n).special_materials[0];
183                         /*video::SMaterial &liquid_material_bfculled =
184                                         *nodedef->get(n).special_materials[1];*/
185                         AtlasPointer &pa_liquid1 =
186                                         *nodedef->get(n).special_aps[0];
187
188                         bool top_is_air = false;
189                         MapNode n = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
190                         if(n.getContent() == CONTENT_AIR)
191                                 top_is_air = true;
192                         
193                         if(top_is_air == false)
194                                 continue;
195
196                         u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio, nodedef));
197                         video::SColor c = MapBlock_LightColor(
198                                         nodedef->get(n).alpha, l);
199                         
200                         video::S3DVertex vertices[4] =
201                         {
202                                 video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
203                                                 pa_liquid1.x0(), pa_liquid1.y1()),
204                                 video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
205                                                 pa_liquid1.x1(), pa_liquid1.y1()),
206                                 video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
207                                                 pa_liquid1.x1(), pa_liquid1.y0()),
208                                 video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
209                                                 pa_liquid1.x0(), pa_liquid1.y0()),
210                         };
211
212                         for(s32 i=0; i<4; i++)
213                         {
214                                 vertices[i].Pos.Y += (-0.5+node_liquid_level)*BS;
215                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
216                         }
217
218                         u16 indices[] = {0,1,2,2,3,0};
219                         // Add to mesh collector
220                         collector.append(liquid_material, vertices, 4, indices, 6);
221                 break;}
222                 case NDT_FLOWINGLIQUID:
223                 {
224                         /*
225                                 Add flowing liquid to mesh
226                         */
227                         assert(nodedef->get(n).special_materials[0]);
228                         assert(nodedef->get(n).special_materials[1]);
229                         assert(nodedef->get(n).special_aps[0]);
230
231                         video::SMaterial &liquid_material =
232                                         *nodedef->get(n).special_materials[0];
233                         video::SMaterial &liquid_material_bfculled =
234                                         *nodedef->get(n).special_materials[1];
235                         AtlasPointer &pa_liquid1 =
236                                         *nodedef->get(n).special_aps[0];
237
238                         bool top_is_same_liquid = false;
239                         MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y+1,z));
240                         content_t c_flowing = nodedef->getId(nodedef->get(n).liquid_alternative_flowing);
241                         content_t c_source = nodedef->getId(nodedef->get(n).liquid_alternative_source);
242                         if(ntop.getContent() == c_flowing || ntop.getContent() == c_source)
243                                 top_is_same_liquid = true;
244                         
245                         u8 l = 0;
246                         // Use the light of the node on top if possible
247                         if(nodedef->get(ntop).param_type == CPT_LIGHT)
248                                 l = decode_light(ntop.getLightBlend(data->m_daynight_ratio, nodedef));
249                         // Otherwise use the light of this node (the liquid)
250                         else
251                                 l = decode_light(n.getLightBlend(data->m_daynight_ratio, nodedef));
252                         video::SColor c = MapBlock_LightColor(
253                                         nodedef->get(n).alpha, l);
254                         
255                         // Neighbor liquid levels (key = relative position)
256                         // Includes current node
257                         core::map<v3s16, f32> neighbor_levels;
258                         core::map<v3s16, content_t> neighbor_contents;
259                         core::map<v3s16, u8> neighbor_flags;
260                         const u8 neighborflag_top_is_same_liquid = 0x01;
261                         v3s16 neighbor_dirs[9] = {
262                                 v3s16(0,0,0),
263                                 v3s16(0,0,1),
264                                 v3s16(0,0,-1),
265                                 v3s16(1,0,0),
266                                 v3s16(-1,0,0),
267                                 v3s16(1,0,1),
268                                 v3s16(-1,0,-1),
269                                 v3s16(1,0,-1),
270                                 v3s16(-1,0,1),
271                         };
272                         for(u32 i=0; i<9; i++)
273                         {
274                                 content_t content = CONTENT_AIR;
275                                 float level = -0.5 * BS;
276                                 u8 flags = 0;
277                                 // Check neighbor
278                                 v3s16 p2 = p + neighbor_dirs[i];
279                                 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
280                                 if(n2.getContent() != CONTENT_IGNORE)
281                                 {
282                                         content = n2.getContent();
283
284                                         if(n2.getContent() == c_source)
285                                                 level = (-0.5+node_liquid_level) * BS;
286                                         else if(n2.getContent() == c_flowing)
287                                                 level = (-0.5 + ((float)(n2.param2&LIQUID_LEVEL_MASK)
288                                                                 + 0.5) / 8.0 * node_liquid_level) * BS;
289
290                                         // Check node above neighbor.
291                                         // NOTE: This doesn't get executed if neighbor
292                                         //       doesn't exist
293                                         p2.Y += 1;
294                                         n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
295                                         if(n2.getContent() == c_source ||
296                                                         n2.getContent() == c_flowing)
297                                                 flags |= neighborflag_top_is_same_liquid;
298                                 }
299                                 
300                                 neighbor_levels.insert(neighbor_dirs[i], level);
301                                 neighbor_contents.insert(neighbor_dirs[i], content);
302                                 neighbor_flags.insert(neighbor_dirs[i], flags);
303                         }
304
305                         // Corner heights (average between four liquids)
306                         f32 corner_levels[4];
307                         
308                         v3s16 halfdirs[4] = {
309                                 v3s16(0,0,0),
310                                 v3s16(1,0,0),
311                                 v3s16(1,0,1),
312                                 v3s16(0,0,1),
313                         };
314                         for(u32 i=0; i<4; i++)
315                         {
316                                 v3s16 cornerdir = halfdirs[i];
317                                 float cornerlevel = 0;
318                                 u32 valid_count = 0;
319                                 u32 air_count = 0;
320                                 for(u32 j=0; j<4; j++)
321                                 {
322                                         v3s16 neighbordir = cornerdir - halfdirs[j];
323                                         content_t content = neighbor_contents[neighbordir];
324                                         // If top is liquid, draw starting from top of node
325                                         if(neighbor_flags[neighbordir] &
326                                                         neighborflag_top_is_same_liquid)
327                                         {
328                                                 cornerlevel = 0.5*BS;
329                                                 valid_count = 1;
330                                                 break;
331                                         }
332                                         // Source is always the same height
333                                         else if(content == c_source)
334                                         {
335                                                 cornerlevel = (-0.5+node_liquid_level)*BS;
336                                                 valid_count = 1;
337                                                 break;
338                                         }
339                                         // Flowing liquid has level information
340                                         else if(content == c_flowing)
341                                         {
342                                                 cornerlevel += neighbor_levels[neighbordir];
343                                                 valid_count++;
344                                         }
345                                         else if(content == CONTENT_AIR)
346                                         {
347                                                 air_count++;
348                                         }
349                                 }
350                                 if(air_count >= 2)
351                                         cornerlevel = -0.5*BS;
352                                 else if(valid_count > 0)
353                                         cornerlevel /= valid_count;
354                                 corner_levels[i] = cornerlevel;
355                         }
356
357                         /*
358                                 Generate sides
359                         */
360
361                         v3s16 side_dirs[4] = {
362                                 v3s16(1,0,0),
363                                 v3s16(-1,0,0),
364                                 v3s16(0,0,1),
365                                 v3s16(0,0,-1),
366                         };
367                         s16 side_corners[4][2] = {
368                                 {1, 2},
369                                 {3, 0},
370                                 {2, 3},
371                                 {0, 1},
372                         };
373                         for(u32 i=0; i<4; i++)
374                         {
375                                 v3s16 dir = side_dirs[i];
376
377                                 /*
378                                         If our topside is liquid and neighbor's topside
379                                         is liquid, don't draw side face
380                                 */
381                                 if(top_is_same_liquid &&
382                                                 neighbor_flags[dir] & neighborflag_top_is_same_liquid)
383                                         continue;
384
385                                 content_t neighbor_content = neighbor_contents[dir];
386                                 const ContentFeatures &n_feat = nodedef->get(neighbor_content);
387                                 
388                                 // Don't draw face if neighbor is blocking the view
389                                 if(n_feat.solidness == 2)
390                                         continue;
391                                 
392                                 bool neighbor_is_same_liquid = (neighbor_content == c_source
393                                                 || neighbor_content == c_flowing);
394                                 
395                                 // Don't draw any faces if neighbor same is liquid and top is
396                                 // same liquid
397                                 if(neighbor_is_same_liquid == true
398                                                 && top_is_same_liquid == false)
399                                         continue;
400
401                                 // Use backface culled material if neighbor doesn't have a
402                                 // solidness of 0
403                                 video::SMaterial *current_material = &liquid_material;
404                                 if(n_feat.solidness != 0 || n_feat.visual_solidness != 0)
405                                         current_material = &liquid_material_bfculled;
406                                 
407                                 video::S3DVertex vertices[4] =
408                                 {
409                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
410                                                         pa_liquid1.x0(), pa_liquid1.y1()),
411                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
412                                                         pa_liquid1.x1(), pa_liquid1.y1()),
413                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
414                                                         pa_liquid1.x1(), pa_liquid1.y0()),
415                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
416                                                         pa_liquid1.x0(), pa_liquid1.y0()),
417                                 };
418                                 
419                                 /*
420                                         If our topside is liquid, set upper border of face
421                                         at upper border of node
422                                 */
423                                 if(top_is_same_liquid)
424                                 {
425                                         vertices[2].Pos.Y = 0.5*BS;
426                                         vertices[3].Pos.Y = 0.5*BS;
427                                 }
428                                 /*
429                                         Otherwise upper position of face is corner levels
430                                 */
431                                 else
432                                 {
433                                         vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
434                                         vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
435                                 }
436                                 
437                                 /*
438                                         If neighbor is liquid, lower border of face is corner
439                                         liquid levels
440                                 */
441                                 if(neighbor_is_same_liquid)
442                                 {
443                                         vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
444                                         vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
445                                 }
446                                 /*
447                                         If neighbor is not liquid, lower border of face is
448                                         lower border of node
449                                 */
450                                 else
451                                 {
452                                         vertices[0].Pos.Y = -0.5*BS;
453                                         vertices[1].Pos.Y = -0.5*BS;
454                                 }
455                                 
456                                 for(s32 j=0; j<4; j++)
457                                 {
458                                         if(dir == v3s16(0,0,1))
459                                                 vertices[j].Pos.rotateXZBy(0);
460                                         if(dir == v3s16(0,0,-1))
461                                                 vertices[j].Pos.rotateXZBy(180);
462                                         if(dir == v3s16(-1,0,0))
463                                                 vertices[j].Pos.rotateXZBy(90);
464                                         if(dir == v3s16(1,0,-0))
465                                                 vertices[j].Pos.rotateXZBy(-90);
466                                                 
467                                         // Do this to not cause glitches when two liquids are
468                                         // side-by-side
469                                         /*if(neighbor_is_same_liquid == false){
470                                                 vertices[j].Pos.X *= 0.98;
471                                                 vertices[j].Pos.Z *= 0.98;
472                                         }*/
473
474                                         vertices[j].Pos += intToFloat(p + blockpos_nodes, BS);
475                                 }
476
477                                 u16 indices[] = {0,1,2,2,3,0};
478                                 // Add to mesh collector
479                                 collector.append(*current_material, vertices, 4, indices, 6);
480                         }
481                         
482                         /*
483                                 Generate top side, if appropriate
484                         */
485                         
486                         if(top_is_same_liquid == false)
487                         {
488                                 video::S3DVertex vertices[4] =
489                                 {
490                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c,
491                                                         pa_liquid1.x0(), pa_liquid1.y1()),
492                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c,
493                                                         pa_liquid1.x1(), pa_liquid1.y1()),
494                                         video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c,
495                                                         pa_liquid1.x1(), pa_liquid1.y0()),
496                                         video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c,
497                                                         pa_liquid1.x0(), pa_liquid1.y0()),
498                                 };
499                                 
500                                 // This fixes a strange bug
501                                 s32 corner_resolve[4] = {3,2,1,0};
502
503                                 for(s32 i=0; i<4; i++)
504                                 {
505                                         //vertices[i].Pos.Y += liquid_level;
506                                         //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
507                                         s32 j = corner_resolve[i];
508                                         vertices[i].Pos.Y += corner_levels[j];
509                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
510                                 }
511
512                                 u16 indices[] = {0,1,2,2,3,0};
513                                 // Add to mesh collector
514                                 collector.append(liquid_material, vertices, 4, indices, 6);
515                         }
516                 break;}
517                 case NDT_GLASSLIKE:
518                 {
519                         video::SMaterial material_glass;
520                         material_glass.setFlag(video::EMF_LIGHTING, false);
521                         material_glass.setFlag(video::EMF_BILINEAR_FILTER, false);
522                         material_glass.setFlag(video::EMF_FOG_ENABLE, true);
523                         material_glass.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
524                         AtlasPointer pa_glass = f.tiles[0].texture;
525                         material_glass.setTexture(0, pa_glass.atlas);
526
527                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef)));
528                         video::SColor c = MapBlock_LightColor(255, l);
529
530                         for(u32 j=0; j<6; j++)
531                         {
532                                 // Check this neighbor
533                                 v3s16 n2p = blockpos_nodes + p + g_6dirs[j];
534                                 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
535                                 // Don't make face if neighbor is of same type
536                                 if(n2.getContent() == n.getContent())
537                                         continue;
538
539                                 // The face at Z+
540                                 video::S3DVertex vertices[4] =
541                                 {
542                                         video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
543                                                 pa_glass.x0(), pa_glass.y1()),
544                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
545                                                 pa_glass.x1(), pa_glass.y1()),
546                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
547                                                 pa_glass.x1(), pa_glass.y0()),
548                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
549                                                 pa_glass.x0(), pa_glass.y0()),
550                                 };
551                                 
552                                 // Rotations in the g_6dirs format
553                                 if(j == 0) // Z+
554                                         for(u16 i=0; i<4; i++)
555                                                 vertices[i].Pos.rotateXZBy(0);
556                                 else if(j == 1) // Y+
557                                         for(u16 i=0; i<4; i++)
558                                                 vertices[i].Pos.rotateYZBy(-90);
559                                 else if(j == 2) // X+
560                                         for(u16 i=0; i<4; i++)
561                                                 vertices[i].Pos.rotateXZBy(-90);
562                                 else if(j == 3) // Z-
563                                         for(u16 i=0; i<4; i++)
564                                                 vertices[i].Pos.rotateXZBy(180);
565                                 else if(j == 4) // Y-
566                                         for(u16 i=0; i<4; i++)
567                                                 vertices[i].Pos.rotateYZBy(90);
568                                 else if(j == 5) // X-
569                                         for(u16 i=0; i<4; i++)
570                                                 vertices[i].Pos.rotateXZBy(90);
571
572                                 for(u16 i=0; i<4; i++){
573                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
574                                 }
575
576                                 u16 indices[] = {0,1,2,2,3,0};
577                                 // Add to mesh collector
578                                 collector.append(material_glass, vertices, 4, indices, 6);
579                         }
580                 break;}
581                 case NDT_ALLFACES:
582                 {
583                         video::SMaterial material_leaves1;
584                         material_leaves1.setFlag(video::EMF_LIGHTING, false);
585                         material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false);
586                         material_leaves1.setFlag(video::EMF_FOG_ENABLE, true);
587                         material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
588                         AtlasPointer pa_leaves1 = f.tiles[0].texture;
589                         material_leaves1.setTexture(0, pa_leaves1.atlas);
590
591                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef)));
592                         video::SColor c = MapBlock_LightColor(255, l);
593
594                         for(u32 j=0; j<6; j++)
595                         {
596                                 video::S3DVertex vertices[4] =
597                                 {
598                                         video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c,
599                                                 pa_leaves1.x0(), pa_leaves1.y1()),
600                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c,
601                                                 pa_leaves1.x1(), pa_leaves1.y1()),
602                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c,
603                                                 pa_leaves1.x1(), pa_leaves1.y0()),
604                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c,
605                                                 pa_leaves1.x0(), pa_leaves1.y0()),
606                                 };
607
608                                 // Rotations in the g_6dirs format
609                                 if(j == 0) // Z+
610                                         for(u16 i=0; i<4; i++)
611                                                 vertices[i].Pos.rotateXZBy(0);
612                                 else if(j == 1) // Y+
613                                         for(u16 i=0; i<4; i++)
614                                                 vertices[i].Pos.rotateYZBy(-90);
615                                 else if(j == 2) // X+
616                                         for(u16 i=0; i<4; i++)
617                                                 vertices[i].Pos.rotateXZBy(-90);
618                                 else if(j == 3) // Z-
619                                         for(u16 i=0; i<4; i++)
620                                                 vertices[i].Pos.rotateXZBy(180);
621                                 else if(j == 4) // Y-
622                                         for(u16 i=0; i<4; i++)
623                                                 vertices[i].Pos.rotateYZBy(90);
624                                 else if(j == 5) // X-
625                                         for(u16 i=0; i<4; i++)
626                                                 vertices[i].Pos.rotateXZBy(90);
627
628                                 for(u16 i=0; i<4; i++){
629                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
630                                 }
631
632                                 u16 indices[] = {0,1,2,2,3,0};
633                                 // Add to mesh collector
634                                 collector.append(material_leaves1, vertices, 4, indices, 6);
635                         }
636                 break;}
637                 case NDT_ALLFACES_OPTIONAL:
638                         // This is always pre-converted to something else
639                         assert(0);
640                         break;
641                 case NDT_TORCHLIKE:
642                 {
643                         v3s16 dir = unpackDir(n.param2);
644                         
645                         AtlasPointer ap(0);
646                         if(dir == v3s16(0,-1,0)){
647                                 ap = f.tiles[0].texture; // floor
648                         } else if(dir == v3s16(0,1,0)){
649                                 ap = f.tiles[1].texture; // ceiling
650                         // For backwards compatibility
651                         } else if(dir == v3s16(0,0,0)){
652                                 ap = f.tiles[0].texture; // floor
653                         } else {
654                                 ap = f.tiles[2].texture; // side
655                         }
656
657                         // Set material
658                         video::SMaterial material;
659                         material.setFlag(video::EMF_LIGHTING, false);
660                         material.setFlag(video::EMF_BACK_FACE_CULLING, false);
661                         material.setFlag(video::EMF_BILINEAR_FILTER, false);
662                         material.setFlag(video::EMF_FOG_ENABLE, true);
663                         //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
664                         material.MaterialType
665                                         = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
666                         material.setTexture(0, ap.atlas);
667
668                         video::SColor c(255,255,255,255);
669
670                         // Wall at X+ of node
671                         video::S3DVertex vertices[4] =
672                         {
673                                 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c,
674                                                 ap.x0(), ap.y1()),
675                                 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c,
676                                                 ap.x1(), ap.y1()),
677                                 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c,
678                                                 ap.x1(), ap.y0()),
679                                 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c,
680                                                 ap.x0(), ap.y0()),
681                         };
682
683                         for(s32 i=0; i<4; i++)
684                         {
685                                 if(dir == v3s16(1,0,0))
686                                         vertices[i].Pos.rotateXZBy(0);
687                                 if(dir == v3s16(-1,0,0))
688                                         vertices[i].Pos.rotateXZBy(180);
689                                 if(dir == v3s16(0,0,1))
690                                         vertices[i].Pos.rotateXZBy(90);
691                                 if(dir == v3s16(0,0,-1))
692                                         vertices[i].Pos.rotateXZBy(-90);
693                                 if(dir == v3s16(0,-1,0))
694                                         vertices[i].Pos.rotateXZBy(45);
695                                 if(dir == v3s16(0,1,0))
696                                         vertices[i].Pos.rotateXZBy(-45);
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, vertices, 4, indices, 6);
704                 break;}
705                 case NDT_SIGNLIKE:
706                 {
707                         // Set material
708                         video::SMaterial material;
709                         material.setFlag(video::EMF_LIGHTING, false);
710                         material.setFlag(video::EMF_BACK_FACE_CULLING, false);
711                         material.setFlag(video::EMF_BILINEAR_FILTER, false);
712                         material.setFlag(video::EMF_FOG_ENABLE, true);
713                         material.MaterialType
714                                         = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
715                         AtlasPointer ap = f.tiles[0].texture;
716                         material.setTexture(0, ap.atlas);
717
718                         u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio, nodedef));
719                         video::SColor c = MapBlock_LightColor(255, l);
720                                 
721                         float d = (float)BS/16;
722                         // Wall at X+ of node
723                         video::S3DVertex vertices[4] =
724                         {
725                                 video::S3DVertex(BS/2-d,BS/2,BS/2, 0,0,0, c,
726                                                 ap.x0(), ap.y0()),
727                                 video::S3DVertex(BS/2-d,BS/2,-BS/2, 0,0,0, c,
728                                                 ap.x1(), ap.y0()),
729                                 video::S3DVertex(BS/2-d,-BS/2,-BS/2, 0,0,0, c,
730                                                 ap.x1(), ap.y1()),
731                                 video::S3DVertex(BS/2-d,-BS/2,BS/2, 0,0,0, c,
732                                                 ap.x0(), ap.y1()),
733                         };
734
735                         v3s16 dir = unpackDir(n.param2);
736
737                         for(s32 i=0; i<4; i++)
738                         {
739                                 if(dir == v3s16(1,0,0))
740                                         vertices[i].Pos.rotateXZBy(0);
741                                 if(dir == v3s16(-1,0,0))
742                                         vertices[i].Pos.rotateXZBy(180);
743                                 if(dir == v3s16(0,0,1))
744                                         vertices[i].Pos.rotateXZBy(90);
745                                 if(dir == v3s16(0,0,-1))
746                                         vertices[i].Pos.rotateXZBy(-90);
747                                 if(dir == v3s16(0,-1,0))
748                                         vertices[i].Pos.rotateXYBy(-90);
749                                 if(dir == v3s16(0,1,0))
750                                         vertices[i].Pos.rotateXYBy(90);
751
752                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
753                         }
754
755                         u16 indices[] = {0,1,2,2,3,0};
756                         // Add to mesh collector
757                         collector.append(material, vertices, 4, indices, 6);
758                 break;}
759                 case NDT_PLANTLIKE:
760                 {
761                         video::SMaterial material_papyrus;
762                         material_papyrus.setFlag(video::EMF_LIGHTING, false);
763                         material_papyrus.setFlag(video::EMF_BILINEAR_FILTER, false);
764                         material_papyrus.setFlag(video::EMF_FOG_ENABLE, true);
765                         material_papyrus.MaterialType=video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
766                         AtlasPointer pa_papyrus = f.tiles[0].texture;
767                         material_papyrus.setTexture(0, pa_papyrus.atlas);
768                         
769                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef)));
770                         video::SColor c = MapBlock_LightColor(255, l);
771
772                         for(u32 j=0; j<4; j++)
773                         {
774                                 video::S3DVertex vertices[4] =
775                                 {
776                                         video::S3DVertex(-BS/2*f.visual_scale,-BS/2,0, 0,0,0, c,
777                                                 pa_papyrus.x0(), pa_papyrus.y1()),
778                                         video::S3DVertex( BS/2*f.visual_scale,-BS/2,0, 0,0,0, c,
779                                                 pa_papyrus.x1(), pa_papyrus.y1()),
780                                         video::S3DVertex( BS/2*f.visual_scale,
781                                                 -BS/2 + f.visual_scale*BS,0, 0,0,0, c,
782                                                 pa_papyrus.x1(), pa_papyrus.y0()),
783                                         video::S3DVertex(-BS/2*f.visual_scale,
784                                                 -BS/2 + f.visual_scale*BS,0, 0,0,0, c,
785                                                 pa_papyrus.x0(), pa_papyrus.y0()),
786                                 };
787
788                                 if(j == 0)
789                                 {
790                                         for(u16 i=0; i<4; i++)
791                                                 vertices[i].Pos.rotateXZBy(45);
792                                 }
793                                 else if(j == 1)
794                                 {
795                                         for(u16 i=0; i<4; i++)
796                                                 vertices[i].Pos.rotateXZBy(-45);
797                                 }
798                                 else if(j == 2)
799                                 {
800                                         for(u16 i=0; i<4; i++)
801                                                 vertices[i].Pos.rotateXZBy(135);
802                                 }
803                                 else if(j == 3)
804                                 {
805                                         for(u16 i=0; i<4; i++)
806                                                 vertices[i].Pos.rotateXZBy(-135);
807                                 }
808
809                                 for(u16 i=0; i<4; i++)
810                                 {
811                                         vertices[i].Pos *= f.visual_scale;
812                                         vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
813                                 }
814
815                                 u16 indices[] = {0,1,2,2,3,0};
816                                 // Add to mesh collector
817                                 collector.append(material_papyrus, vertices, 4, indices, 6);
818                         }
819                 break;}
820                 case NDT_FENCELIKE:
821                 {
822                         video::SMaterial material_wood;
823                         material_wood.setFlag(video::EMF_LIGHTING, false);
824                         material_wood.setFlag(video::EMF_BILINEAR_FILTER, false);
825                         material_wood.setFlag(video::EMF_FOG_ENABLE, true);
826                         material_wood.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
827                         AtlasPointer pa_wood = f.tiles[0].texture;
828                         material_wood.setTexture(0, pa_wood.atlas);
829
830                         u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef)));
831                         video::SColor c = MapBlock_LightColor(255, l);
832
833                         const f32 post_rad=(f32)BS/10;
834                         const f32 bar_rad=(f32)BS/20;
835                         const f32 bar_len=(f32)(BS/2)-post_rad;
836
837                         // The post - always present
838                         v3f pos = intToFloat(p+blockpos_nodes, BS);
839                         f32 postuv[24]={
840                                         0.4,0.4,0.6,0.6,
841                                         0.35,0,0.65,1,
842                                         0.35,0,0.65,1,
843                                         0.35,0,0.65,1,
844                                         0.35,0,0.65,1,
845                                         0.4,0.4,0.6,0.6};
846                         makeCuboid(material_wood, &collector,
847                                 &pa_wood, c, pos,
848                                 post_rad,BS/2,post_rad, postuv);
849
850                         // Now a section of fence, +X, if there's a post there
851                         v3s16 p2 = p;
852                         p2.X++;
853                         MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
854                         const ContentFeatures *f2 = &nodedef->get(n2);
855                         if(f2->drawtype == NDT_FENCELIKE)
856                         {
857                                 pos = intToFloat(p+blockpos_nodes, BS);
858                                 pos.X += BS/2;
859                                 pos.Y += BS/4;
860                                 f32 xrailuv[24]={
861                                         0,0.4,1,0.6,
862                                         0,0.4,1,0.6,
863                                         0,0.4,1,0.6,
864                                         0,0.4,1,0.6,
865                                         0,0.4,1,0.6,
866                                         0,0.4,1,0.6};
867                                 makeCuboid(material_wood, &collector,
868                                         &pa_wood, c, pos,
869                                         bar_len,bar_rad,bar_rad, xrailuv);
870
871                                 pos.Y -= BS/2;
872                                 makeCuboid(material_wood, &collector,
873                                         &pa_wood, c, pos,
874                                         bar_len,bar_rad,bar_rad, xrailuv);
875                         }
876
877                         // Now a section of fence, +Z, if there's a post there
878                         p2 = p;
879                         p2.Z++;
880                         n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
881                         f2 = &nodedef->get(n2);
882                         if(f2->drawtype == NDT_FENCELIKE)
883                         {
884                                 pos = intToFloat(p+blockpos_nodes, BS);
885                                 pos.Z += BS/2;
886                                 pos.Y += BS/4;
887                                 f32 zrailuv[24]={
888                                         0,0.4,1,0.6,
889                                         0,0.4,1,0.6,
890                                         0,0.4,1,0.6,
891                                         0,0.4,1,0.6,
892                                         0,0.4,1,0.6,
893                                         0,0.4,1,0.6};
894                                 makeCuboid(material_wood, &collector,
895                                         &pa_wood, c, pos,
896                                         bar_rad,bar_rad,bar_len, zrailuv);
897                                 pos.Y -= BS/2;
898                                 makeCuboid(material_wood, &collector,
899                                         &pa_wood, c, pos,
900                                         bar_rad,bar_rad,bar_len, zrailuv);
901
902                         }
903                 break;}
904                 case NDT_RAILLIKE:
905                 {
906                         bool is_rail_x [] = { false, false };  /* x-1, x+1 */
907                         bool is_rail_z [] = { false, false };  /* z-1, z+1 */
908
909                         MapNode n_minus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x-1,y,z));
910                         MapNode n_plus_x = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x+1,y,z));
911                         MapNode n_minus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z-1));
912                         MapNode n_plus_z = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(x,y,z+1));
913                         
914                         content_t thiscontent = n.getContent();
915                         if(n_minus_x.getContent() == thiscontent)
916                                 is_rail_x[0] = true;
917                         if(n_plus_x.getContent() == thiscontent)
918                                 is_rail_x[1] = true;
919                         if(n_minus_z.getContent() == thiscontent)
920                                 is_rail_z[0] = true;
921                         if(n_plus_z.getContent() == thiscontent)
922                                 is_rail_z[1] = true;
923
924                         int adjacencies = is_rail_x[0] + is_rail_x[1] + is_rail_z[0] + is_rail_z[1];
925
926                         // Assign textures
927                         AtlasPointer ap = f.tiles[0].texture; // straight
928                         if(adjacencies < 2)
929                                 ap = f.tiles[0].texture; // straight
930                         else if(adjacencies == 2)
931                         {
932                                 if((is_rail_x[0] && is_rail_x[1]) || (is_rail_z[0] && is_rail_z[1]))
933                                         ap = f.tiles[0].texture; // straight
934                                 else
935                                         ap = f.tiles[1].texture; // curved
936                         }
937                         else if(adjacencies == 3)
938                                 ap = f.tiles[2].texture; // t-junction
939                         else if(adjacencies == 4)
940                                 ap = f.tiles[3].texture; // crossing
941                         
942                         video::SMaterial material_rail;
943                         material_rail.setFlag(video::EMF_LIGHTING, false);
944                         material_rail.setFlag(video::EMF_BACK_FACE_CULLING, false);
945                         material_rail.setFlag(video::EMF_BILINEAR_FILTER, false);
946                         material_rail.setFlag(video::EMF_FOG_ENABLE, true);
947                         material_rail.MaterialType
948                                         = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
949                         material_rail.setTexture(0, ap.atlas);
950
951                         u8 l = decode_light(n.getLightBlend(data->m_daynight_ratio, nodedef));
952                         video::SColor c = MapBlock_LightColor(255, l);
953
954                         float d = (float)BS/16;
955                         video::S3DVertex vertices[4] =
956                         {
957                                 video::S3DVertex(-BS/2,-BS/2+d,-BS/2, 0,0,0, c,
958                                                 ap.x0(), ap.y1()),
959                                 video::S3DVertex(BS/2,-BS/2+d,-BS/2, 0,0,0, c,
960                                                 ap.x1(), ap.y1()),
961                                 video::S3DVertex(BS/2,-BS/2+d,BS/2, 0,0,0, c,
962                                                 ap.x1(), ap.y0()),
963                                 video::S3DVertex(-BS/2,-BS/2+d,BS/2, 0,0,0, c,
964                                                 ap.x0(), ap.y0()),
965                         };
966
967                         // Rotate textures
968                         int angle = 0;
969
970                         if(adjacencies == 1)
971                         {
972                                 if(is_rail_x[0] || is_rail_x[1])
973                                         angle = 90;
974                         }
975                         else if(adjacencies == 2)
976                         {
977                                 if(is_rail_x[0] && is_rail_x[1])
978                                         angle = 90;
979                                 else if(is_rail_x[0] && is_rail_z[0])
980                                         angle = 270;
981                                 else if(is_rail_x[0] && is_rail_z[1])
982                                         angle = 180;
983                                 else if(is_rail_x[1] && is_rail_z[1])
984                                         angle = 90;
985                         }
986                         else if(adjacencies == 3)
987                         {
988                                 if(!is_rail_x[0])
989                                         angle=0;
990                                 if(!is_rail_x[1])
991                                         angle=180;
992                                 if(!is_rail_z[0])
993                                         angle=90;
994                                 if(!is_rail_z[1])
995                                         angle=270;
996                         }
997
998                         if(angle != 0) {
999                                 for(u16 i=0; i<4; i++)
1000                                         vertices[i].Pos.rotateXZBy(angle);
1001                         }
1002
1003                         for(s32 i=0; i<4; i++)
1004                         {
1005                                 vertices[i].Pos += intToFloat(p + blockpos_nodes, BS);
1006                         }
1007
1008                         u16 indices[] = {0,1,2,2,3,0};
1009                         collector.append(material_rail, vertices, 4, indices, 6);
1010                 break;}
1011                 }
1012         }
1013 }
1014 #endif
1015