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