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