Isolate irrlicht references and use a singleton (#6041)
[oweals/minetest.git] / src / content_mapblock.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 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 Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser 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 "util/numeric.h"
22 #include "util/directiontables.h"
23 #include "mapblock_mesh.h"
24 #include "settings.h"
25 #include "nodedef.h"
26 #include "client/tile.h"
27 #include "mesh.h"
28 #include <IMeshManipulator.h>
29 #include "client/renderingengine.h"
30 #include "client.h"
31 #include "log.h"
32 #include "noise.h"
33
34 // Distance of light extrapolation (for oversized nodes)
35 // After this distance, it gives up and considers light level constant
36 #define SMOOTH_LIGHTING_OVERSIZE 1.0
37
38 // Node edge count (for glasslike-framed)
39 #define FRAMED_EDGE_COUNT 12
40
41 // Node neighbor count, including edge-connected, but not vertex-connected
42 // (for glasslike-framed)
43 // Corresponding offsets are listed in g_27dirs
44 #define FRAMED_NEIGHBOR_COUNT 18
45
46 static const v3s16 light_dirs[8] = {
47         v3s16(-1, -1, -1),
48         v3s16(-1, -1,  1),
49         v3s16(-1,  1, -1),
50         v3s16(-1,  1,  1),
51         v3s16( 1, -1, -1),
52         v3s16( 1, -1,  1),
53         v3s16( 1,  1, -1),
54         v3s16( 1,  1,  1),
55 };
56
57 // Standard index set to make a quad on 4 vertices
58 static constexpr u16 quad_indices[] = {0, 1, 2, 2, 3, 0};
59
60 const std::string MapblockMeshGenerator::raillike_groupname = "connect_to_raillike";
61
62 MapblockMeshGenerator::MapblockMeshGenerator(MeshMakeData *input, MeshCollector *output)
63 {
64         data      = input;
65         collector = output;
66
67         nodedef   = data->m_client->ndef();
68         meshmanip = RenderingEngine::get_scene_manager()->getMeshManipulator();
69
70         enable_mesh_cache = g_settings->getBool("enable_mesh_cache") &&
71                 !data->m_smooth_lighting; // Mesh cache is not supported with smooth lighting
72
73         blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
74 }
75
76 void MapblockMeshGenerator::useTile(int index, bool disable_backface_culling)
77 {
78         getNodeTileN(n, p, index, data, tile);
79         if (!data->m_smooth_lighting)
80                 color = encode_light(light, f->light_source);
81         for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
82                 tile.layers[layer].material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
83                 if (disable_backface_culling)
84                         tile.layers[layer].material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
85         }
86 }
87
88 void MapblockMeshGenerator::useDefaultTile(bool set_color)
89 {
90         getNodeTile(n, p, v3s16(0, 0, 0), data, tile);
91         if (set_color && !data->m_smooth_lighting)
92                 color = encode_light(light, f->light_source);
93 }
94
95 void MapblockMeshGenerator::getTile(const v3s16& direction, TileSpec &tile)
96 {
97         getNodeTile(n, p, direction, data, tile);
98 }
99
100 void MapblockMeshGenerator::drawQuad(v3f *coords, const v3s16 &normal)
101 {
102         static const v2f tcoords[4] = {v2f(0, 0), v2f(1, 0), v2f(1, 1), v2f(0, 1)};
103         video::S3DVertex vertices[4];
104         bool shade_face = !f->light_source && (normal != v3s16(0, 0, 0));
105         v3f normal2(normal.X, normal.Y, normal.Z);
106         for (int j = 0; j < 4; j++) {
107                 vertices[j].Pos = coords[j] + origin;
108                 vertices[j].Normal = normal2;
109                 if (data->m_smooth_lighting)
110                         vertices[j].Color = blendLightColor(coords[j]);
111                 else
112                         vertices[j].Color = color;
113                 if (shade_face)
114                         applyFacesShading(vertices[j].Color, normal2);
115                 vertices[j].TCoords = tcoords[j];
116         }
117         collector->append(tile, vertices, 4, quad_indices, 6);
118 }
119
120 // Create a cuboid.
121 //  tiles     - the tiles (materials) to use (for all 6 faces)
122 //  tilecount - number of entries in tiles, 1<=tilecount<=6
123 //  lights    - vertex light levels. The order is the same as in light_dirs.
124 //              NULL may be passed if smooth lighting is disabled.
125 //  txc       - texture coordinates - this is a list of texture coordinates
126 //              for the opposite corners of each face - therefore, there
127 //              should be (2+2)*6=24 values in the list. The order of
128 //              the faces in the list is up-down-right-left-back-front
129 //              (compatible with ContentFeatures).
130 void MapblockMeshGenerator::drawCuboid(const aabb3f &box,
131         TileSpec *tiles, int tilecount, const u16 *lights, const f32 *txc)
132 {
133         assert(tilecount >= 1 && tilecount <= 6); // pre-condition
134
135         v3f min = box.MinEdge;
136         v3f max = box.MaxEdge;
137
138         video::SColor colors[6];
139         if (!data->m_smooth_lighting) {
140                 for (int face = 0; face != 6; ++face) {
141                         colors[face] = encode_light(light, f->light_source);
142                 }
143                 if (!f->light_source) {
144                         applyFacesShading(colors[0], v3f(0, 1, 0));
145                         applyFacesShading(colors[1], v3f(0, -1, 0));
146                         applyFacesShading(colors[2], v3f(1, 0, 0));
147                         applyFacesShading(colors[3], v3f(-1, 0, 0));
148                         applyFacesShading(colors[4], v3f(0, 0, 1));
149                         applyFacesShading(colors[5], v3f(0, 0, -1));
150                 }
151         }
152
153         video::S3DVertex vertices[24] = {
154                 // top
155                 video::S3DVertex(min.X, max.Y, max.Z, 0, 1, 0, colors[0], txc[0], txc[1]),
156                 video::S3DVertex(max.X, max.Y, max.Z, 0, 1, 0, colors[0], txc[2], txc[1]),
157                 video::S3DVertex(max.X, max.Y, min.Z, 0, 1, 0, colors[0], txc[2], txc[3]),
158                 video::S3DVertex(min.X, max.Y, min.Z, 0, 1, 0, colors[0], txc[0], txc[3]),
159                 // bottom
160                 video::S3DVertex(min.X, min.Y, min.Z, 0, -1, 0, colors[1], txc[4], txc[5]),
161                 video::S3DVertex(max.X, min.Y, min.Z, 0, -1, 0, colors[1], txc[6], txc[5]),
162                 video::S3DVertex(max.X, min.Y, max.Z, 0, -1, 0, colors[1], txc[6], txc[7]),
163                 video::S3DVertex(min.X, min.Y, max.Z, 0, -1, 0, colors[1], txc[4], txc[7]),
164                 // right
165                 video::S3DVertex(max.X, max.Y, min.Z, 1, 0, 0, colors[2], txc[ 8], txc[9]),
166                 video::S3DVertex(max.X, max.Y, max.Z, 1, 0, 0, colors[2], txc[10], txc[9]),
167                 video::S3DVertex(max.X, min.Y, max.Z, 1, 0, 0, colors[2], txc[10], txc[11]),
168                 video::S3DVertex(max.X, min.Y, min.Z, 1, 0, 0, colors[2], txc[ 8], txc[11]),
169                 // left
170                 video::S3DVertex(min.X, max.Y, max.Z, -1, 0, 0, colors[3], txc[12], txc[13]),
171                 video::S3DVertex(min.X, max.Y, min.Z, -1, 0, 0, colors[3], txc[14], txc[13]),
172                 video::S3DVertex(min.X, min.Y, min.Z, -1, 0, 0, colors[3], txc[14], txc[15]),
173                 video::S3DVertex(min.X, min.Y, max.Z, -1, 0, 0, colors[3], txc[12], txc[15]),
174                 // back
175                 video::S3DVertex(max.X, max.Y, max.Z, 0, 0, 1, colors[4], txc[16], txc[17]),
176                 video::S3DVertex(min.X, max.Y, max.Z, 0, 0, 1, colors[4], txc[18], txc[17]),
177                 video::S3DVertex(min.X, min.Y, max.Z, 0, 0, 1, colors[4], txc[18], txc[19]),
178                 video::S3DVertex(max.X, min.Y, max.Z, 0, 0, 1, colors[4], txc[16], txc[19]),
179                 // front
180                 video::S3DVertex(min.X, max.Y, min.Z, 0, 0, -1, colors[5], txc[20], txc[21]),
181                 video::S3DVertex(max.X, max.Y, min.Z, 0, 0, -1, colors[5], txc[22], txc[21]),
182                 video::S3DVertex(max.X, min.Y, min.Z, 0, 0, -1, colors[5], txc[22], txc[23]),
183                 video::S3DVertex(min.X, min.Y, min.Z, 0, 0, -1, colors[5], txc[20], txc[23]),
184         };
185
186         static const u8 light_indices[24] = {
187                 3, 7, 6, 2,
188                 0, 4, 5, 1,
189                 6, 7, 5, 4,
190                 3, 2, 0, 1,
191                 7, 3, 1, 5,
192                 2, 6, 4, 0
193         };
194
195         for (int face = 0; face < 6; face++) {
196                 int tileindex = MYMIN(face, tilecount - 1);
197                 const TileSpec &tile = tiles[tileindex];
198                 for (int j = 0; j < 4; j++) {
199                         video::S3DVertex &vertex = vertices[face * 4 + j];
200                         v2f &tcoords = vertex.TCoords;
201                         switch (tile.rotation) {
202                         case 0:
203                                 break;
204                         case 1: // R90
205                                 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
206                                 break;
207                         case 2: // R180
208                                 tcoords.rotateBy(180, irr::core::vector2df(0, 0));
209                                 break;
210                         case 3: // R270
211                                 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
212                                 break;
213                         case 4: // FXR90
214                                 tcoords.X = 1.0 - tcoords.X;
215                                 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
216                                 break;
217                         case 5: // FXR270
218                                 tcoords.X = 1.0 - tcoords.X;
219                                 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
220                                 break;
221                         case 6: // FYR90
222                                 tcoords.Y = 1.0 - tcoords.Y;
223                                 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
224                                 break;
225                         case 7: // FYR270
226                                 tcoords.Y = 1.0 - tcoords.Y;
227                                 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
228                                 break;
229                         case 8: // FX
230                                 tcoords.X = 1.0 - tcoords.X;
231                                 break;
232                         case 9: // FY
233                                 tcoords.Y = 1.0 - tcoords.Y;
234                                 break;
235                         default:
236                                 break;
237                         }
238                 }
239         }
240
241         if (data->m_smooth_lighting) {
242                 for (int j = 0; j < 24; ++j) {
243                         vertices[j].Color = encode_light(lights[light_indices[j]],
244                                 f->light_source);
245                         if (!f->light_source)
246                                 applyFacesShading(vertices[j].Color, vertices[j].Normal);
247                 }
248         }
249
250         // Add to mesh collector
251         for (int k = 0; k < 6; ++k) {
252                 int tileindex = MYMIN(k, tilecount - 1);
253                 collector->append(tiles[tileindex], vertices + 4 * k, 4, quad_indices, 6);
254         }
255 }
256
257 // Gets the base lighting values for a node
258 void MapblockMeshGenerator::getSmoothLightFrame()
259 {
260         for (int k = 0; k < 8; ++k) {
261                 u16 light = getSmoothLight(blockpos_nodes + p, light_dirs[k], data);
262                 frame.lightsA[k] = light & 0xff;
263                 frame.lightsB[k] = light >> 8;
264         }
265 }
266
267 // Calculates vertex light level
268 //  vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
269 u16 MapblockMeshGenerator::blendLight(const v3f &vertex_pos)
270 {
271         f32 x = core::clamp(vertex_pos.X / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
272         f32 y = core::clamp(vertex_pos.Y / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
273         f32 z = core::clamp(vertex_pos.Z / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
274         f32 lightA = 0.0;
275         f32 lightB = 0.0;
276         for (int k = 0; k < 8; ++k) {
277                 f32 dx = (k & 4) ? x : 1 - x;
278                 f32 dy = (k & 2) ? y : 1 - y;
279                 f32 dz = (k & 1) ? z : 1 - z;
280                 lightA += dx * dy * dz * frame.lightsA[k];
281                 lightB += dx * dy * dz * frame.lightsB[k];
282         }
283         return
284                 core::clamp(core::round32(lightA), 0, 255) |
285                 core::clamp(core::round32(lightB), 0, 255) << 8;
286 }
287
288 // Calculates vertex color to be used in mapblock mesh
289 //  vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
290 //  tile_color - node's tile color
291 video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos)
292 {
293         u16 light = blendLight(vertex_pos);
294         return encode_light(light, f->light_source);
295 }
296
297 video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos,
298         const v3f &vertex_normal)
299 {
300         video::SColor color = blendLightColor(vertex_pos);
301         if (!f->light_source)
302                 applyFacesShading(color, vertex_normal);
303         return color;
304 }
305
306 void MapblockMeshGenerator::generateCuboidTextureCoords(const aabb3f &box, f32 *coords)
307 {
308         f32 tx1 = (box.MinEdge.X / BS) + 0.5;
309         f32 ty1 = (box.MinEdge.Y / BS) + 0.5;
310         f32 tz1 = (box.MinEdge.Z / BS) + 0.5;
311         f32 tx2 = (box.MaxEdge.X / BS) + 0.5;
312         f32 ty2 = (box.MaxEdge.Y / BS) + 0.5;
313         f32 tz2 = (box.MaxEdge.Z / BS) + 0.5;
314         f32 txc[24] = {
315                     tx1, 1 - tz2,     tx2, 1 - tz1, // up
316                     tx1,     tz1,     tx2,     tz2, // down
317                     tz1, 1 - ty2,     tz2, 1 - ty1, // right
318                 1 - tz2, 1 - ty2, 1 - tz1, 1 - ty1, // left
319                 1 - tx2, 1 - ty2, 1 - tx1, 1 - ty1, // back
320                     tx1, 1 - ty2,     tx2, 1 - ty1, // front
321         };
322         for (int i = 0; i != 24; ++i)
323                 coords[i] = txc[i];
324 }
325
326 void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc,
327         TileSpec *tiles, int tile_count)
328 {
329         f32 texture_coord_buf[24];
330         f32 dx1 = box.MinEdge.X;
331         f32 dy1 = box.MinEdge.Y;
332         f32 dz1 = box.MinEdge.Z;
333         f32 dx2 = box.MaxEdge.X;
334         f32 dy2 = box.MaxEdge.Y;
335         f32 dz2 = box.MaxEdge.Z;
336         box.MinEdge += origin;
337         box.MaxEdge += origin;
338         if (!txc) {
339                 generateCuboidTextureCoords(box, texture_coord_buf);
340                 txc = texture_coord_buf;
341         }
342         if (!tiles) {
343                 tiles = &tile;
344                 tile_count = 1;
345         }
346         if (data->m_smooth_lighting) {
347                 u16 lights[8];
348                 for (int j = 0; j < 8; ++j) {
349                         v3f d;
350                         d.X = (j & 4) ? dx2 : dx1;
351                         d.Y = (j & 2) ? dy2 : dy1;
352                         d.Z = (j & 1) ? dz2 : dz1;
353                         lights[j] = blendLight(d);
354                 }
355                 drawCuboid(box, tiles, tile_count, lights, txc);
356         } else {
357                 drawCuboid(box, tiles, tile_count, NULL, txc);
358         }
359 }
360
361 /*!
362  * Returns the i-th special tile for a map node.
363  */
364 static TileSpec getSpecialTile(const ContentFeatures &f,
365         const MapNode &n, u8 i)
366 {
367         TileSpec copy = f.special_tiles[i];
368         for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
369                 TileLayer *layer = &copy.layers[layernum];
370                 if (layer->texture_id == 0)
371                         continue;
372                 if (!layer->has_color)
373                         n.getColor(f, &(layer->color));
374         }
375         return copy;
376 }
377
378 void MapblockMeshGenerator::prepareLiquidNodeDrawing()
379 {
380         tile_liquid_top = getSpecialTile(*f, n, 0);
381         tile_liquid = getSpecialTile(*f, n, 1);
382
383         MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y + 1, p.Z));
384         c_flowing = nodedef->getId(f->liquid_alternative_flowing);
385         c_source = nodedef->getId(f->liquid_alternative_source);
386         top_is_same_liquid = (ntop.getContent() == c_flowing) || (ntop.getContent() == c_source);
387
388         if (data->m_smooth_lighting)
389                 return; // don't need to pre-compute anything in this case
390
391         if (f->light_source != 0) {
392                 // If this liquid emits light and doesn't contain light, draw
393                 // it at what it emits, for an increased effect
394                 light = decode_light(f->light_source);
395                 light = light | (light << 8);
396         } else if (nodedef->get(ntop).param_type == CPT_LIGHT) {
397                 // Otherwise, use the light of the node on top if possible
398                 light = getInteriorLight(ntop, 0, nodedef);
399         }
400
401         color_liquid_top = encode_light(light, f->light_source);
402         color = encode_light(light, f->light_source);
403 }
404
405 void MapblockMeshGenerator::getLiquidNeighborhood()
406 {
407         u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8);
408
409         for (int w = -1; w <= 1; w++)
410         for (int u = -1; u <= 1; u++) {
411                 NeighborData &neighbor = liquid_neighbors[w + 1][u + 1];
412                 v3s16 p2 = p + v3s16(u, 0, w);
413                 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
414                 neighbor.content = n2.getContent();
415                 neighbor.level = -0.5 * BS;
416                 neighbor.is_same_liquid = false;
417                 neighbor.top_is_same_liquid = false;
418
419                 if (neighbor.content == CONTENT_IGNORE)
420                         continue;
421
422                 if (neighbor.content == c_source) {
423                         neighbor.is_same_liquid = true;
424                         neighbor.level = 0.5 * BS;
425                 } else if (neighbor.content == c_flowing) {
426                         neighbor.is_same_liquid = true;
427                         u8 liquid_level = (n2.param2 & LIQUID_LEVEL_MASK);
428                         if (liquid_level <= LIQUID_LEVEL_MAX + 1 - range)
429                                 liquid_level = 0;
430                         else
431                                 liquid_level -= (LIQUID_LEVEL_MAX + 1 - range);
432                         neighbor.level = (-0.5 + (liquid_level + 0.5) / range) * BS;
433                 }
434
435                 // Check node above neighbor.
436                 // NOTE: This doesn't get executed if neighbor
437                 //       doesn't exist
438                 p2.Y++;
439                 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
440                 if (n2.getContent() == c_source || n2.getContent() == c_flowing)
441                         neighbor.top_is_same_liquid = true;
442         }
443 }
444
445 void MapblockMeshGenerator::calculateCornerLevels()
446 {
447         for (int k = 0; k < 2; k++)
448         for (int i = 0; i < 2; i++)
449                 corner_levels[k][i] = getCornerLevel(i, k);
450 }
451
452 f32 MapblockMeshGenerator::getCornerLevel(int i, int k)
453 {
454         float sum = 0;
455         int count = 0;
456         int air_count = 0;
457         for (int dk = 0; dk < 2; dk++)
458         for (int di = 0; di < 2; di++) {
459                 NeighborData &neighbor_data = liquid_neighbors[k + dk][i + di];
460                 content_t content = neighbor_data.content;
461
462                 // If top is liquid, draw starting from top of node
463                 if (neighbor_data.top_is_same_liquid)
464                         return 0.5 * BS;
465
466                 // Source always has the full height
467                 if (content == c_source)
468                         return 0.5 * BS;
469
470                 // Flowing liquid has level information
471                 if (content == c_flowing) {
472                         sum += neighbor_data.level;
473                         count++;
474                 } else if (content == CONTENT_AIR) {
475                         air_count++;
476                         if (air_count >= 2)
477                                 return -0.5 * BS + 0.2;
478                 }
479         }
480         if (count > 0)
481                 return sum / count;
482         return 0;
483 }
484
485 void MapblockMeshGenerator::drawLiquidSides()
486 {
487         struct LiquidFaceDesc {
488                 v3s16 dir; // XZ
489                 v3s16 p[2]; // XZ only; 1 means +, 0 means -
490         };
491         struct UV {
492                 int u, v;
493         };
494         static const LiquidFaceDesc base_faces[4] = {
495                 {v3s16( 1, 0,  0), {v3s16(1, 0, 1), v3s16(1, 0, 0)}},
496                 {v3s16(-1, 0,  0), {v3s16(0, 0, 0), v3s16(0, 0, 1)}},
497                 {v3s16( 0, 0,  1), {v3s16(0, 0, 1), v3s16(1, 0, 1)}},
498                 {v3s16( 0, 0, -1), {v3s16(1, 0, 0), v3s16(0, 0, 0)}},
499         };
500         static const UV base_vertices[4] = {
501                 {0, 1},
502                 {1, 1},
503                 {1, 0},
504                 {0, 0}
505         };
506         for (int i = 0; i < 4; i++) {
507                 const LiquidFaceDesc &face = base_faces[i];
508                 const NeighborData &neighbor = liquid_neighbors[face.dir.Z + 1][face.dir.X + 1];
509
510                 // No face between nodes of the same liquid, unless there is node
511                 // at the top to which it should be connected. Again, unless the face
512                 // there would be inside the liquid
513                 if (neighbor.is_same_liquid) {
514                         if (!top_is_same_liquid)
515                                 continue;
516                         if (neighbor.top_is_same_liquid)
517                                 continue;
518                 }
519
520                 const ContentFeatures &neighbor_features = nodedef->get(neighbor.content);
521                 // Don't draw face if neighbor is blocking the view
522                 if (neighbor_features.solidness == 2)
523                         continue;
524
525                 video::S3DVertex vertices[4];
526                 for (int j = 0; j < 4; j++) {
527                         const UV &vertex = base_vertices[j];
528                         const v3s16 &base = face.p[vertex.u];
529                         v3f pos;
530                         pos.X = (base.X - 0.5) * BS;
531                         pos.Z = (base.Z - 0.5) * BS;
532                         if (vertex.v)
533                                 pos.Y = neighbor.is_same_liquid ? corner_levels[base.Z][base.X] : -0.5 * BS;
534                         else
535                                 pos.Y =     !top_is_same_liquid ? corner_levels[base.Z][base.X] :  0.5 * BS;
536                         if (data->m_smooth_lighting)
537                                 color = blendLightColor(pos);
538                         pos += origin;
539                         vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, color, vertex.u, vertex.v);
540                 };
541                 collector->append(tile_liquid, vertices, 4, quad_indices, 6);
542         }
543 }
544
545 void MapblockMeshGenerator::drawLiquidTop()
546 {
547         // To get backface culling right, the vertices need to go
548         // clockwise around the front of the face. And we happened to
549         // calculate corner levels in exact reverse order.
550         static const int corner_resolve[4][2] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
551
552         video::S3DVertex vertices[4] = {
553                 video::S3DVertex(-BS / 2, 0,  BS / 2, 0, 0, 0, color_liquid_top, 0, 1),
554                 video::S3DVertex( BS / 2, 0,  BS / 2, 0, 0, 0, color_liquid_top, 1, 1),
555                 video::S3DVertex( BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 1, 0),
556                 video::S3DVertex(-BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 0, 0),
557         };
558
559         for (int i = 0; i < 4; i++) {
560                 int u = corner_resolve[i][0];
561                 int w = corner_resolve[i][1];
562                 vertices[i].Pos.Y += corner_levels[w][u];
563                 if (data->m_smooth_lighting)
564                         vertices[i].Color = blendLightColor(vertices[i].Pos);
565                 vertices[i].Pos += origin;
566         }
567
568         // Default downwards-flowing texture animation goes from
569         // -Z towards +Z, thus the direction is +Z.
570         // Rotate texture to make animation go in flow direction
571         // Positive if liquid moves towards +Z
572         f32 dz = (corner_levels[0][0] + corner_levels[0][1]) -
573                  (corner_levels[1][0] + corner_levels[1][1]);
574         // Positive if liquid moves towards +X
575         f32 dx = (corner_levels[0][0] + corner_levels[1][0]) -
576                  (corner_levels[0][1] + corner_levels[1][1]);
577         f32 tcoord_angle = atan2(dz, dx) * core::RADTODEG;
578         v2f tcoord_center(0.5, 0.5);
579         v2f tcoord_translate(blockpos_nodes.Z + p.Z, blockpos_nodes.X + p.X);
580         tcoord_translate.rotateBy(tcoord_angle);
581         tcoord_translate.X -= floor(tcoord_translate.X);
582         tcoord_translate.Y -= floor(tcoord_translate.Y);
583
584         for (int i = 0; i < 4; i++) {
585                 vertices[i].TCoords.rotateBy(tcoord_angle, tcoord_center);
586                 vertices[i].TCoords += tcoord_translate;
587         }
588
589         std::swap(vertices[0].TCoords, vertices[2].TCoords);
590
591         collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
592 }
593
594 void MapblockMeshGenerator::drawLiquidNode()
595 {
596         prepareLiquidNodeDrawing();
597         getLiquidNeighborhood();
598         calculateCornerLevels();
599         drawLiquidSides();
600         if (!top_is_same_liquid)
601                 drawLiquidTop();
602 }
603
604 void MapblockMeshGenerator::drawGlasslikeNode()
605 {
606         useDefaultTile();
607
608         for (int face = 0; face < 6; face++) {
609                 // Check this neighbor
610                 v3s16 dir = g_6dirs[face];
611                 v3s16 neighbor_pos = blockpos_nodes + p + dir;
612                 MapNode neighbor = data->m_vmanip.getNodeNoExNoEmerge(neighbor_pos);
613                 // Don't make face if neighbor is of same type
614                 if (neighbor.getContent() == n.getContent())
615                         continue;
616                 // Face at Z-
617                 v3f vertices[4] = {
618                         v3f(-BS / 2,  BS / 2, -BS / 2),
619                         v3f( BS / 2,  BS / 2, -BS / 2),
620                         v3f( BS / 2, -BS / 2, -BS / 2),
621                         v3f(-BS / 2, -BS / 2, -BS / 2),
622                 };
623                 for (int i = 0; i < 4; i++) {
624                         switch (face) {
625                                 case D6D_ZP: vertices[i].rotateXZBy(180); break;
626                                 case D6D_YP: vertices[i].rotateYZBy( 90); break;
627                                 case D6D_XP: vertices[i].rotateXZBy( 90); break;
628                                 case D6D_ZN: vertices[i].rotateXZBy(  0); break;
629                                 case D6D_YN: vertices[i].rotateYZBy(-90); break;
630                                 case D6D_XN: vertices[i].rotateXZBy(-90); break;
631                         }
632                 }
633                 drawQuad(vertices, dir);
634         }
635 }
636
637 void MapblockMeshGenerator::drawGlasslikeFramedNode()
638 {
639         TileSpec tiles[6];
640         for (int face = 0; face < 6; face++)
641                 getTile(g_6dirs[face], tiles[face]);
642
643         TileSpec glass_tiles[6];
644         if (tiles[1].layers[0].texture &&
645                         tiles[2].layers[0].texture &&
646                         tiles[3].layers[0].texture) {
647                 glass_tiles[0] = tiles[4];
648                 glass_tiles[1] = tiles[0];
649                 glass_tiles[2] = tiles[4];
650                 glass_tiles[3] = tiles[4];
651                 glass_tiles[4] = tiles[3];
652                 glass_tiles[5] = tiles[4];
653         } else {
654                 for (int face = 0; face < 6; face++)
655                         glass_tiles[face] = tiles[4];
656         }
657
658         u8 param2 = n.getParam2();
659         bool H_merge = !(param2 & 128);
660         bool V_merge = !(param2 & 64);
661         param2 &= 63;
662
663         static const float a = BS / 2;
664         static const float g = a - 0.003;
665         static const float b = .876 * ( BS / 2 );
666
667         static const aabb3f frame_edges[FRAMED_EDGE_COUNT] = {
668                 aabb3f( b,  b, -a,  a,  a,  a), // y+
669                 aabb3f(-a,  b, -a, -b,  a,  a), // y+
670                 aabb3f( b, -a, -a,  a, -b,  a), // y-
671                 aabb3f(-a, -a, -a, -b, -b,  a), // y-
672                 aabb3f( b, -a,  b,  a,  a,  a), // x+
673                 aabb3f( b, -a, -a,  a,  a, -b), // x+
674                 aabb3f(-a, -a,  b, -b,  a,  a), // x-
675                 aabb3f(-a, -a, -a, -b,  a, -b), // x-
676                 aabb3f(-a,  b,  b,  a,  a,  a), // z+
677                 aabb3f(-a, -a,  b,  a, -b,  a), // z+
678                 aabb3f(-a, -a, -a,  a, -b, -b), // z-
679                 aabb3f(-a,  b, -a,  a,  a, -b), // z-
680         };
681         static const aabb3f glass_faces[6] = {
682                 aabb3f(-g, -g,  g,  g,  g,  g), // z+
683                 aabb3f(-g,  g, -g,  g,  g,  g), // y+
684                 aabb3f( g, -g, -g,  g,  g,  g), // x+
685                 aabb3f(-g, -g, -g,  g,  g, -g), // z-
686                 aabb3f(-g, -g, -g,  g, -g,  g), // y-
687                 aabb3f(-g, -g, -g, -g,  g,  g), // x-
688         };
689
690         // tables of neighbour (connect if same type and merge allowed),
691         // checked with g_26dirs
692
693         // 1 = connect, 0 = face visible
694         bool nb[FRAMED_NEIGHBOR_COUNT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
695
696         // 1 = check
697         static const bool check_nb_vertical   [FRAMED_NEIGHBOR_COUNT] = {0,1,0,0,1,0, 0,0,0,0, 0,0,0,0, 0,0,0,0};
698         static const bool check_nb_horizontal [FRAMED_NEIGHBOR_COUNT] = {1,0,1,1,0,1, 0,0,0,0, 1,1,1,1, 0,0,0,0};
699         static const bool check_nb_all        [FRAMED_NEIGHBOR_COUNT] = {1,1,1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1};
700         const bool *check_nb = check_nb_all;
701
702         // neighbours checks for frames visibility
703         if (H_merge || V_merge) {
704                 if (!H_merge)
705                         check_nb = check_nb_vertical; // vertical-only merge
706                 if (!V_merge)
707                         check_nb = check_nb_horizontal; // horizontal-only merge
708                 content_t current = n.getContent();
709                 for (int i = 0; i < FRAMED_NEIGHBOR_COUNT; i++) {
710                         if (!check_nb[i])
711                                 continue;
712                         v3s16 n2p = blockpos_nodes + p + g_26dirs[i];
713                         MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
714                         content_t n2c = n2.getContent();
715                         if (n2c == current || n2c == CONTENT_IGNORE)
716                                 nb[i] = 1;
717                 }
718         }
719
720         // edge visibility
721
722         static const u8 nb_triplet[FRAMED_EDGE_COUNT][3] = {
723                 {1, 2,  7}, {1, 5,  6}, {4, 2, 15}, {4, 5, 14},
724                 {2, 0, 11}, {2, 3, 13}, {5, 0, 10}, {5, 3, 12},
725                 {0, 1,  8}, {0, 4, 16}, {3, 4, 17}, {3, 1,  9},
726         };
727
728         tile = tiles[1];
729         for (int edge = 0; edge < FRAMED_EDGE_COUNT; edge++) {
730                 bool edge_invisible;
731                 if (nb[nb_triplet[edge][2]])
732                         edge_invisible = nb[nb_triplet[edge][0]] & nb[nb_triplet[edge][1]];
733                 else
734                         edge_invisible = nb[nb_triplet[edge][0]] ^ nb[nb_triplet[edge][1]];
735                 if (edge_invisible)
736                         continue;
737                 drawAutoLightedCuboid(frame_edges[edge]);
738         }
739
740         for (int face = 0; face < 6; face++) {
741                 if (nb[face])
742                         continue;
743                 tile = glass_tiles[face];
744                 drawAutoLightedCuboid(glass_faces[face]);
745         }
746
747         // Optionally render internal liquid level defined by param2
748         // Liquid is textured with 1 tile defined in nodedef 'special_tiles'
749         if (param2 > 0 && f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL &&
750                         f->special_tiles[0].layers[0].texture) {
751                 // Internal liquid level has param2 range 0 .. 63,
752                 // convert it to -0.5 .. 0.5
753                 float vlev = (param2 / 63.0) * 2.0 - 1.0;
754                 tile = getSpecialTile(*f, n, 0);
755                 drawAutoLightedCuboid(aabb3f(-(nb[5] ? g : b),
756                                              -(nb[4] ? g : b),
757                                              -(nb[3] ? g : b),
758                                               (nb[2] ? g : b),
759                                               (nb[1] ? g : b) * vlev,
760                                               (nb[0] ? g : b)));
761         }
762 }
763
764 void MapblockMeshGenerator::drawAllfacesNode()
765 {
766         static const aabb3f box(-BS / 2, -BS / 2, -BS / 2, BS / 2, BS / 2, BS / 2);
767         useDefaultTile(false);
768         drawAutoLightedCuboid(box);
769 }
770
771 void MapblockMeshGenerator::drawTorchlikeNode()
772 {
773         u8 wall = n.getWallMounted(nodedef);
774         u8 tileindex = 0;
775         switch (wall) {
776                 case DWM_YP: tileindex = 1; break; // ceiling
777                 case DWM_YN: tileindex = 0; break; // floor
778                 default:     tileindex = 2; // side (or invalid—should we care?)
779         }
780         useTile(tileindex, true);
781
782         float size = BS / 2 * f->visual_scale;
783         v3f vertices[4] = {
784                 v3f(-size,  size, 0),
785                 v3f( size,  size, 0),
786                 v3f( size, -size, 0),
787                 v3f(-size, -size, 0),
788         };
789         for (int i = 0; i < 4; i++) {
790                 switch (wall) {
791                         case DWM_YP: vertices[i].rotateXZBy(-45); break;
792                         case DWM_YN: vertices[i].rotateXZBy( 45); break;
793                         case DWM_XP: vertices[i].rotateXZBy(  0); break;
794                         case DWM_XN: vertices[i].rotateXZBy(180); break;
795                         case DWM_ZP: vertices[i].rotateXZBy( 90); break;
796                         case DWM_ZN: vertices[i].rotateXZBy(-90); break;
797                 }
798         }
799         drawQuad(vertices);
800 }
801
802 void MapblockMeshGenerator::drawSignlikeNode()
803 {
804         u8 wall = n.getWallMounted(nodedef);
805         useTile(0, true);
806         static const float offset = BS / 16;
807         float size = BS / 2 * f->visual_scale;
808         // Wall at X+ of node
809         v3f vertices[4] = {
810                 v3f(BS / 2 - offset,  size,  size),
811                 v3f(BS / 2 - offset,  size, -size),
812                 v3f(BS / 2 - offset, -size, -size),
813                 v3f(BS / 2 - offset, -size,  size),
814         };
815         for (int i = 0; i < 4; i++) {
816                 switch (wall) {
817                         case DWM_YP: vertices[i].rotateXYBy( 90); break;
818                         case DWM_YN: vertices[i].rotateXYBy(-90); break;
819                         case DWM_XP: vertices[i].rotateXZBy(  0); break;
820                         case DWM_XN: vertices[i].rotateXZBy(180); break;
821                         case DWM_ZP: vertices[i].rotateXZBy( 90); break;
822                         case DWM_ZN: vertices[i].rotateXZBy(-90); break;
823                 }
824         }
825         drawQuad(vertices);
826 }
827
828 void MapblockMeshGenerator::drawPlantlikeQuad(float rotation, float quad_offset,
829         bool offset_top_only)
830 {
831         v3f vertices[4] = {
832                 v3f(-scale, -BS / 2 + scale * 2, 0),
833                 v3f( scale, -BS / 2 + scale * 2, 0),
834                 v3f( scale, -BS / 2, 0),
835                 v3f(-scale, -BS / 2, 0),
836         };
837         if (random_offset_Y) {
838                 PseudoRandom yrng(face_num++ | p.X << 16 | p.Z << 8 | p.Y << 24);
839                 offset.Y = -BS * ((yrng.next() % 16 / 16.0) * 0.125);
840         }
841         int offset_count = offset_top_only ? 2 : 4;
842         for (int i = 0; i < offset_count; i++)
843                 vertices[i].Z += quad_offset;
844         for (int i = 0; i < 4; i++) {
845                 vertices[i].rotateXZBy(rotation + rotate_degree);
846                 vertices[i] += offset;
847         }
848         drawQuad(vertices);
849 }
850
851 void MapblockMeshGenerator::drawPlantlikeNode()
852 {
853         useTile(0, false);
854         draw_style = PLANT_STYLE_CROSS;
855         scale = BS / 2 * f->visual_scale;
856         offset = v3f(0, 0, 0);
857         rotate_degree = 0;
858         random_offset_Y = false;
859         face_num = 0;
860
861         switch (f->param_type_2) {
862         case CPT2_MESHOPTIONS:
863                 draw_style = PlantlikeStyle(n.param2 & MO_MASK_STYLE);
864                 if (n.param2 & MO_BIT_SCALE_SQRT2)
865                         scale *= 1.41421;
866                 if (n.param2 & MO_BIT_RANDOM_OFFSET) {
867                         PseudoRandom rng(p.X << 8 | p.Z | p.Y << 16);
868                         offset.X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
869                         offset.Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
870                 }
871                 if (n.param2 & MO_BIT_RANDOM_OFFSET_Y)
872                         random_offset_Y = true;
873                 break;
874
875         case CPT2_DEGROTATE:
876                 rotate_degree = n.param2 * 2;
877                 break;
878
879         default:
880                 break;
881         }
882
883         switch (draw_style) {
884         case PLANT_STYLE_CROSS:
885                 drawPlantlikeQuad(46);
886                 drawPlantlikeQuad(-44);
887                 break;
888
889         case PLANT_STYLE_CROSS2:
890                 drawPlantlikeQuad(91);
891                 drawPlantlikeQuad(1);
892                 break;
893
894         case PLANT_STYLE_STAR:
895                 drawPlantlikeQuad(121);
896                 drawPlantlikeQuad(241);
897                 drawPlantlikeQuad(1);
898                 break;
899
900         case PLANT_STYLE_HASH:
901                 drawPlantlikeQuad(  1, BS / 4);
902                 drawPlantlikeQuad( 91, BS / 4);
903                 drawPlantlikeQuad(181, BS / 4);
904                 drawPlantlikeQuad(271, BS / 4);
905                 break;
906
907         case PLANT_STYLE_HASH2:
908                 drawPlantlikeQuad(  1, -BS / 2, true);
909                 drawPlantlikeQuad( 91, -BS / 2, true);
910                 drawPlantlikeQuad(181, -BS / 2, true);
911                 drawPlantlikeQuad(271, -BS / 2, true);
912                 break;
913         }
914 }
915
916 void MapblockMeshGenerator::drawFirelikeQuad(float rotation, float opening_angle,
917         float offset_h, float offset_v)
918 {
919         v3f vertices[4] = {
920                 v3f(-scale, -BS / 2 + scale * 2, 0),
921                 v3f( scale, -BS / 2 + scale * 2, 0),
922                 v3f( scale, -BS / 2, 0),
923                 v3f(-scale, -BS / 2, 0),
924         };
925         for (int i = 0; i < 4; i++) {
926                 vertices[i].rotateYZBy(opening_angle);
927                 vertices[i].Z += offset_h;
928                 vertices[i].rotateXZBy(rotation);
929                 vertices[i].Y += offset_v;
930         }
931         drawQuad(vertices);
932 }
933
934 void MapblockMeshGenerator::drawFirelikeNode()
935 {
936         useTile(0, false);
937         scale = BS / 2 * f->visual_scale;
938
939         // Check for adjacent nodes
940         bool neighbors = false;
941         bool neighbor[6] = {0, 0, 0, 0, 0, 0};
942         content_t current = n.getContent();
943         for (int i = 0; i < 6; i++) {
944                 v3s16 n2p = blockpos_nodes + p + g_6dirs[i];
945                 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
946                 content_t n2c = n2.getContent();
947                 if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
948                         neighbor[i] = true;
949                         neighbors = true;
950                 }
951         }
952         bool drawBasicFire = neighbor[D6D_YN] || !neighbors;
953         bool drawBottomFire = neighbor[D6D_YP];
954
955         if (drawBasicFire || neighbor[D6D_ZP])
956                 drawFirelikeQuad(0, -10, 0.4 * BS);
957         else if (drawBottomFire)
958                 drawFirelikeQuad(0, 70, 0.47 * BS, 0.484 * BS);
959
960         if (drawBasicFire || neighbor[D6D_XN])
961                 drawFirelikeQuad(90, -10, 0.4 * BS);
962         else if (drawBottomFire)
963                 drawFirelikeQuad(90, 70, 0.47 * BS, 0.484 * BS);
964
965         if (drawBasicFire || neighbor[D6D_ZN])
966                 drawFirelikeQuad(180, -10, 0.4 * BS);
967         else if (drawBottomFire)
968                 drawFirelikeQuad(180, 70, 0.47 * BS, 0.484 * BS);
969
970         if (drawBasicFire || neighbor[D6D_XP])
971                 drawFirelikeQuad(270, -10, 0.4 * BS);
972         else if (drawBottomFire)
973                 drawFirelikeQuad(270, 70, 0.47 * BS, 0.484 * BS);
974
975         if (drawBasicFire) {
976                 drawFirelikeQuad(45, 0, 0.0);
977                 drawFirelikeQuad(-45, 0, 0.0);
978         }
979 }
980
981 void MapblockMeshGenerator::drawFencelikeNode()
982 {
983         useDefaultTile(false);
984         TileSpec tile_nocrack = tile;
985         for (int layer = 0; layer < MAX_TILE_LAYERS; layer++)
986                 tile_nocrack.layers[layer].material_flags &= ~MATERIAL_FLAG_CRACK;
987
988         // Put wood the right way around in the posts
989         TileSpec tile_rot = tile;
990         tile_rot.rotation = 1;
991
992         static const f32 post_rad = BS / 8;
993         static const f32 bar_rad  = BS / 16;
994         static const f32 bar_len  = BS / 2 - post_rad;
995
996         // The post - always present
997         static const aabb3f post(-post_rad, -BS / 2, -post_rad,
998                                   post_rad,  BS / 2,  post_rad);
999         static const f32 postuv[24] = {
1000                 0.375, 0.375, 0.625, 0.625,
1001                 0.375, 0.375, 0.625, 0.625,
1002                 0.000, 0.000, 0.250, 1.000,
1003                 0.250, 0.000, 0.500, 1.000,
1004                 0.500, 0.000, 0.750, 1.000,
1005                 0.750, 0.000, 1.000, 1.000,
1006         };
1007         tile = tile_rot;
1008         drawAutoLightedCuboid(post, postuv);
1009
1010         tile = tile_nocrack;
1011
1012         // Now a section of fence, +X, if there's a post there
1013         v3s16 p2 = p;
1014         p2.X++;
1015         MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1016         const ContentFeatures *f2 = &nodedef->get(n2);
1017         if (f2->drawtype == NDT_FENCELIKE) {
1018                 static const aabb3f bar_x1(BS / 2 - bar_len,  BS / 4 - bar_rad, -bar_rad,
1019                                            BS / 2 + bar_len,  BS / 4 + bar_rad,  bar_rad);
1020                 static const aabb3f bar_x2(BS / 2 - bar_len, -BS / 4 - bar_rad, -bar_rad,
1021                                            BS / 2 + bar_len, -BS / 4 + bar_rad,  bar_rad);
1022                 static const f32 xrailuv[24] = {
1023                         0.000, 0.125, 1.000, 0.250,
1024                         0.000, 0.250, 1.000, 0.375,
1025                         0.375, 0.375, 0.500, 0.500,
1026                         0.625, 0.625, 0.750, 0.750,
1027                         0.000, 0.500, 1.000, 0.625,
1028                         0.000, 0.875, 1.000, 1.000,
1029                 };
1030                 drawAutoLightedCuboid(bar_x1, xrailuv);
1031                 drawAutoLightedCuboid(bar_x2, xrailuv);
1032         }
1033
1034         // Now a section of fence, +Z, if there's a post there
1035         p2 = p;
1036         p2.Z++;
1037         n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1038         f2 = &nodedef->get(n2);
1039         if (f2->drawtype == NDT_FENCELIKE) {
1040                 static const aabb3f bar_z1(-bar_rad,  BS / 4 - bar_rad, BS / 2 - bar_len,
1041                                             bar_rad,  BS / 4 + bar_rad, BS / 2 + bar_len);
1042                 static const aabb3f bar_z2(-bar_rad, -BS / 4 - bar_rad, BS / 2 - bar_len,
1043                                             bar_rad, -BS / 4 + bar_rad, BS / 2 + bar_len);
1044                 static const f32 zrailuv[24] = {
1045                         0.1875, 0.0625, 0.3125, 0.3125, // cannot rotate; stretch
1046                         0.2500, 0.0625, 0.3750, 0.3125, // for wood texture instead
1047                         0.0000, 0.5625, 1.0000, 0.6875,
1048                         0.0000, 0.3750, 1.0000, 0.5000,
1049                         0.3750, 0.3750, 0.5000, 0.5000,
1050                         0.6250, 0.6250, 0.7500, 0.7500,
1051                 };
1052                 drawAutoLightedCuboid(bar_z1, zrailuv);
1053                 drawAutoLightedCuboid(bar_z2, zrailuv);
1054         }
1055 }
1056
1057 bool MapblockMeshGenerator::isSameRail(v3s16 dir)
1058 {
1059         MapNode node2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
1060         if (node2.getContent() == n.getContent())
1061                 return true;
1062         const ContentFeatures &def2 = nodedef->get(node2);
1063         return ((def2.drawtype == NDT_RAILLIKE) &&
1064                 (def2.getGroup(raillike_groupname) == raillike_group));
1065 }
1066
1067 void MapblockMeshGenerator::drawRaillikeNode()
1068 {
1069         static const v3s16 direction[4] = {
1070                 v3s16( 0, 0,  1),
1071                 v3s16( 0, 0, -1),
1072                 v3s16(-1, 0,  0),
1073                 v3s16( 1, 0,  0),
1074         };
1075         static const int slope_angle[4] = {0, 180, 90, -90};
1076
1077         enum RailTile {
1078                 straight,
1079                 curved,
1080                 junction,
1081                 cross,
1082         };
1083         struct RailDesc {
1084                 int tile_index;
1085                 int angle;
1086         };
1087         static const RailDesc rail_kinds[16] = {
1088                                    // +x -x -z +z
1089                                    //-------------
1090                 {straight,   0}, //  .  .  .  .
1091                 {straight,   0}, //  .  .  . +Z
1092                 {straight,   0}, //  .  . -Z  .
1093                 {straight,   0}, //  .  . -Z +Z
1094                 {straight,  90}, //  . -X  .  .
1095                 {  curved, 180}, //  . -X  . +Z
1096                 {  curved, 270}, //  . -X -Z  .
1097                 {junction, 180}, //  . -X -Z +Z
1098                 {straight,  90}, // +X  .  .  .
1099                 {  curved,  90}, // +X  .  . +Z
1100                 {  curved,   0}, // +X  . -Z  .
1101                 {junction,   0}, // +X  . -Z +Z
1102                 {straight,  90}, // +X -X  .  .
1103                 {junction,  90}, // +X -X  . +Z
1104                 {junction, 270}, // +X -X -Z  .
1105                 {   cross,   0}, // +X -X -Z +Z
1106         };
1107
1108         raillike_group = nodedef->get(n).getGroup(raillike_groupname);
1109
1110         int code = 0;
1111         int angle;
1112         int tile_index;
1113         bool sloped = false;
1114         for (int dir = 0; dir < 4; dir++) {
1115                 bool rail_above = isSameRail(direction[dir] + v3s16(0, 1, 0));
1116                 if (rail_above) {
1117                         sloped = true;
1118                         angle = slope_angle[dir];
1119                 }
1120                 if (rail_above ||
1121                                 isSameRail(direction[dir]) ||
1122                                 isSameRail(direction[dir] + v3s16(0, -1, 0)))
1123                         code |= 1 << dir;
1124         }
1125
1126         if (sloped) {
1127                 tile_index = straight;
1128         } else {
1129                 tile_index = rail_kinds[code].tile_index;
1130                 angle = rail_kinds[code].angle;
1131         }
1132
1133         useTile(tile_index, true);
1134
1135         static const float offset = BS / 64;
1136         static const float size   = BS / 2;
1137         float y2 = sloped ? size : -size;
1138         v3f vertices[4] = {
1139                 v3f(-size,    y2 + offset,  size),
1140                 v3f( size,    y2 + offset,  size),
1141                 v3f( size, -size + offset, -size),
1142                 v3f(-size, -size + offset, -size),
1143         };
1144         if (angle)
1145                 for (int i = 0; i < 4; i++)
1146                         vertices[i].rotateXZBy(angle);
1147         drawQuad(vertices);
1148 }
1149
1150 void MapblockMeshGenerator::drawNodeboxNode()
1151 {
1152         static const v3s16 tile_dirs[6] = {
1153                 v3s16(0, 1, 0),
1154                 v3s16(0, -1, 0),
1155                 v3s16(1, 0, 0),
1156                 v3s16(-1, 0, 0),
1157                 v3s16(0, 0, 1),
1158                 v3s16(0, 0, -1)
1159         };
1160
1161         // we have this order for some reason...
1162         static const v3s16 connection_dirs[6] = {
1163                 v3s16( 0,  1,  0), // top
1164                 v3s16( 0, -1,  0), // bottom
1165                 v3s16( 0,  0, -1), // front
1166                 v3s16(-1,  0,  0), // left
1167                 v3s16( 0,  0,  1), // back
1168                 v3s16( 1,  0,  0), // right
1169         };
1170
1171         TileSpec tiles[6];
1172         for (int face = 0; face < 6; face++) {
1173                 // Handles facedir rotation for textures
1174                 getTile(tile_dirs[face], tiles[face]);
1175         }
1176
1177         // locate possible neighboring nodes to connect to
1178         int neighbors_set = 0;
1179         if (f->node_box.type == NODEBOX_CONNECTED) {
1180                 for (int dir = 0; dir != 6; dir++) {
1181                         int flag = 1 << dir;
1182                         v3s16 p2 = blockpos_nodes + p + connection_dirs[dir];
1183                         MapNode n2 = data->m_vmanip.getNodeNoEx(p2);
1184                         if (nodedef->nodeboxConnects(n, n2, flag))
1185                                 neighbors_set |= flag;
1186                 }
1187         }
1188
1189         std::vector<aabb3f> boxes;
1190         n.getNodeBoxes(nodedef, &boxes, neighbors_set);
1191         for (std::vector<aabb3f>::iterator i = boxes.begin(); i != boxes.end(); ++i)
1192                 drawAutoLightedCuboid(*i, NULL, tiles, 6);
1193 }
1194
1195 void MapblockMeshGenerator::drawMeshNode()
1196 {
1197         u8 facedir = 0;
1198         scene::IMesh* mesh;
1199         bool private_mesh; // as a grab/drop pair is not thread-safe
1200
1201         if (f->param_type_2 == CPT2_FACEDIR ||
1202                         f->param_type_2 == CPT2_COLORED_FACEDIR) {
1203                 facedir = n.getFaceDir(nodedef);
1204         } else if (f->param_type_2 == CPT2_WALLMOUNTED ||
1205                         f->param_type_2 == CPT2_COLORED_WALLMOUNTED) {
1206                 // Convert wallmounted to 6dfacedir.
1207                 // When cache enabled, it is already converted.
1208                 facedir = n.getWallMounted(nodedef);
1209                 if (!enable_mesh_cache) {
1210                         static const u8 wm_to_6d[6] = {20, 0, 16 + 1, 12 + 3, 8, 4 + 2};
1211                         facedir = wm_to_6d[facedir];
1212                 }
1213         }
1214
1215         if (!data->m_smooth_lighting && f->mesh_ptr[facedir]) {
1216                 // use cached meshes
1217                 private_mesh = false;
1218                 mesh = f->mesh_ptr[facedir];
1219         } else if (f->mesh_ptr[0]) {
1220                 // no cache, clone and rotate mesh
1221                 private_mesh = true;
1222                 mesh = cloneMesh(f->mesh_ptr[0]);
1223                 rotateMeshBy6dFacedir(mesh, facedir);
1224                 recalculateBoundingBox(mesh);
1225                 meshmanip->recalculateNormals(mesh, true, false);
1226         } else
1227                 return;
1228
1229         int mesh_buffer_count = mesh->getMeshBufferCount();
1230         for (int j = 0; j < mesh_buffer_count; j++) {
1231                 useTile(j, false);
1232                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1233                 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1234                 int vertex_count = buf->getVertexCount();
1235
1236                 if (data->m_smooth_lighting) {
1237                         // Mesh is always private here. So the lighting is applied to each
1238                         // vertex right here.
1239                         for (int k = 0; k < vertex_count; k++) {
1240                                 video::S3DVertex &vertex = vertices[k];
1241                                 vertex.Color = blendLightColor(vertex.Pos, vertex.Normal);
1242                                 vertex.Pos += origin;
1243                         }
1244                         collector->append(tile, vertices, vertex_count,
1245                                 buf->getIndices(), buf->getIndexCount());
1246                 } else {
1247                         // Don't modify the mesh, it may not be private here.
1248                         // Instead, let the collector process colors, etc.
1249                         collector->append(tile, vertices, vertex_count,
1250                                 buf->getIndices(), buf->getIndexCount(), origin,
1251                                 color, f->light_source);
1252                 }
1253         }
1254         if (private_mesh)
1255                 mesh->drop();
1256 }
1257
1258 // also called when the drawtype is known but should have been pre-converted
1259 void MapblockMeshGenerator::errorUnknownDrawtype()
1260 {
1261         infostream << "Got drawtype " << f->drawtype << std::endl;
1262         FATAL_ERROR("Unknown drawtype");
1263 }
1264
1265 void MapblockMeshGenerator::drawNode()
1266 {
1267         if (data->m_smooth_lighting)
1268                 getSmoothLightFrame();
1269         else
1270                 light = getInteriorLight(n, 1, nodedef);
1271         switch (f->drawtype) {
1272                 case NDT_FLOWINGLIQUID:     drawLiquidNode(); break;
1273                 case NDT_GLASSLIKE:         drawGlasslikeNode(); break;
1274                 case NDT_GLASSLIKE_FRAMED:  drawGlasslikeFramedNode(); break;
1275                 case NDT_ALLFACES:          drawAllfacesNode(); break;
1276                 case NDT_TORCHLIKE:         drawTorchlikeNode(); break;
1277                 case NDT_SIGNLIKE:          drawSignlikeNode(); break;
1278                 case NDT_PLANTLIKE:         drawPlantlikeNode(); break;
1279                 case NDT_FIRELIKE:          drawFirelikeNode(); break;
1280                 case NDT_FENCELIKE:         drawFencelikeNode(); break;
1281                 case NDT_RAILLIKE:          drawRaillikeNode(); break;
1282                 case NDT_NODEBOX:           drawNodeboxNode(); break;
1283                 case NDT_MESH:              drawMeshNode(); break;
1284                 default:                    errorUnknownDrawtype(); break;
1285         }
1286 }
1287
1288 /*
1289         TODO: Fix alpha blending for special nodes
1290         Currently only the last element rendered is blended correct
1291 */
1292 void MapblockMeshGenerator::generate()
1293 {
1294         for (p.Z = 0; p.Z < MAP_BLOCKSIZE; p.Z++)
1295         for (p.Y = 0; p.Y < MAP_BLOCKSIZE; p.Y++)
1296         for (p.X = 0; p.X < MAP_BLOCKSIZE; p.X++) {
1297                 n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1298                 f = &nodedef->get(n);
1299                 // Solid nodes are drawn by MapBlockMesh
1300                 if (f->solidness != 0)
1301                         continue;
1302                 if (f->drawtype == NDT_AIRLIKE)
1303                         continue;
1304                 origin = intToFloat(p, BS);
1305                 drawNode();
1306         }
1307 }