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