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