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