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