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