]> git.lizzy.rs Git - minetest.git/blob - src/client/content_mapblock.cpp
8x block meshes (#13133)
[minetest.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 //  mask      - a bit mask that suppresses drawing of tiles.
154 //              tile i will not be drawn if mask & (1 << i) is 1
155 void MapblockMeshGenerator::drawCuboid(const aabb3f &box,
156         TileSpec *tiles, int tilecount, const LightInfo *lights, const f32 *txc, u8 mask)
157 {
158         assert(tilecount >= 1 && tilecount <= 6); // pre-condition
159
160         v3f min = box.MinEdge;
161         v3f max = box.MaxEdge;
162
163         video::SColor colors[6];
164         if (!data->m_smooth_lighting) {
165                 for (int face = 0; face != 6; ++face) {
166                         colors[face] = encode_light(light, f->light_source);
167                 }
168                 if (!f->light_source) {
169                         applyFacesShading(colors[0], v3f(0, 1, 0));
170                         applyFacesShading(colors[1], v3f(0, -1, 0));
171                         applyFacesShading(colors[2], v3f(1, 0, 0));
172                         applyFacesShading(colors[3], v3f(-1, 0, 0));
173                         applyFacesShading(colors[4], v3f(0, 0, 1));
174                         applyFacesShading(colors[5], v3f(0, 0, -1));
175                 }
176         }
177
178         video::S3DVertex vertices[24] = {
179                 // top
180                 video::S3DVertex(min.X, max.Y, max.Z, 0, 1, 0, colors[0], txc[0], txc[1]),
181                 video::S3DVertex(max.X, max.Y, max.Z, 0, 1, 0, colors[0], txc[2], txc[1]),
182                 video::S3DVertex(max.X, max.Y, min.Z, 0, 1, 0, colors[0], txc[2], txc[3]),
183                 video::S3DVertex(min.X, max.Y, min.Z, 0, 1, 0, colors[0], txc[0], txc[3]),
184                 // bottom
185                 video::S3DVertex(min.X, min.Y, min.Z, 0, -1, 0, colors[1], txc[4], txc[5]),
186                 video::S3DVertex(max.X, min.Y, min.Z, 0, -1, 0, colors[1], txc[6], txc[5]),
187                 video::S3DVertex(max.X, min.Y, max.Z, 0, -1, 0, colors[1], txc[6], txc[7]),
188                 video::S3DVertex(min.X, min.Y, max.Z, 0, -1, 0, colors[1], txc[4], txc[7]),
189                 // right
190                 video::S3DVertex(max.X, max.Y, min.Z, 1, 0, 0, colors[2], txc[ 8], txc[9]),
191                 video::S3DVertex(max.X, max.Y, max.Z, 1, 0, 0, colors[2], txc[10], txc[9]),
192                 video::S3DVertex(max.X, min.Y, max.Z, 1, 0, 0, colors[2], txc[10], txc[11]),
193                 video::S3DVertex(max.X, min.Y, min.Z, 1, 0, 0, colors[2], txc[ 8], txc[11]),
194                 // left
195                 video::S3DVertex(min.X, max.Y, max.Z, -1, 0, 0, colors[3], txc[12], txc[13]),
196                 video::S3DVertex(min.X, max.Y, min.Z, -1, 0, 0, colors[3], txc[14], txc[13]),
197                 video::S3DVertex(min.X, min.Y, min.Z, -1, 0, 0, colors[3], txc[14], txc[15]),
198                 video::S3DVertex(min.X, min.Y, max.Z, -1, 0, 0, colors[3], txc[12], txc[15]),
199                 // back
200                 video::S3DVertex(max.X, max.Y, max.Z, 0, 0, 1, colors[4], txc[16], txc[17]),
201                 video::S3DVertex(min.X, max.Y, max.Z, 0, 0, 1, colors[4], txc[18], txc[17]),
202                 video::S3DVertex(min.X, min.Y, max.Z, 0, 0, 1, colors[4], txc[18], txc[19]),
203                 video::S3DVertex(max.X, min.Y, max.Z, 0, 0, 1, colors[4], txc[16], txc[19]),
204                 // front
205                 video::S3DVertex(min.X, max.Y, min.Z, 0, 0, -1, colors[5], txc[20], txc[21]),
206                 video::S3DVertex(max.X, max.Y, min.Z, 0, 0, -1, colors[5], txc[22], txc[21]),
207                 video::S3DVertex(max.X, min.Y, min.Z, 0, 0, -1, colors[5], txc[22], txc[23]),
208                 video::S3DVertex(min.X, min.Y, min.Z, 0, 0, -1, colors[5], txc[20], txc[23]),
209         };
210
211         static const u8 light_indices[24] = {
212                 3, 7, 6, 2,
213                 0, 4, 5, 1,
214                 6, 7, 5, 4,
215                 3, 2, 0, 1,
216                 7, 3, 1, 5,
217                 2, 6, 4, 0
218         };
219
220         for (int face = 0; face < 6; face++) {
221                 int tileindex = MYMIN(face, tilecount - 1);
222                 const TileSpec &tile = tiles[tileindex];
223                 for (int j = 0; j < 4; j++) {
224                         video::S3DVertex &vertex = vertices[face * 4 + j];
225                         v2f &tcoords = vertex.TCoords;
226                         switch (tile.rotation) {
227                         case 0:
228                                 break;
229                         case 1: // R90
230                                 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
231                                 break;
232                         case 2: // R180
233                                 tcoords.rotateBy(180, irr::core::vector2df(0, 0));
234                                 break;
235                         case 3: // R270
236                                 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
237                                 break;
238                         case 4: // FXR90
239                                 tcoords.X = 1.0 - tcoords.X;
240                                 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
241                                 break;
242                         case 5: // FXR270
243                                 tcoords.X = 1.0 - tcoords.X;
244                                 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
245                                 break;
246                         case 6: // FYR90
247                                 tcoords.Y = 1.0 - tcoords.Y;
248                                 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
249                                 break;
250                         case 7: // FYR270
251                                 tcoords.Y = 1.0 - tcoords.Y;
252                                 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
253                                 break;
254                         case 8: // FX
255                                 tcoords.X = 1.0 - tcoords.X;
256                                 break;
257                         case 9: // FY
258                                 tcoords.Y = 1.0 - tcoords.Y;
259                                 break;
260                         default:
261                                 break;
262                         }
263                 }
264         }
265
266         if (data->m_smooth_lighting) {
267                 for (int j = 0; j < 24; ++j) {
268                         video::S3DVertex &vertex = vertices[j];
269                         vertex.Color = encode_light(
270                                 lights[light_indices[j]].getPair(MYMAX(0.0f, vertex.Normal.Y)),
271                                 f->light_source);
272                         if (!f->light_source)
273                                 applyFacesShading(vertex.Color, vertex.Normal);
274                 }
275         }
276
277         // Add to mesh collector
278         for (int k = 0; k < 6; ++k) {
279                 if (mask & (1 << k))
280                         continue;
281                 int tileindex = MYMIN(k, tilecount - 1);
282                 collector->append(tiles[tileindex], vertices + 4 * k, 4, quad_indices, 6);
283         }
284 }
285
286 // Gets the base lighting values for a node
287 void MapblockMeshGenerator::getSmoothLightFrame()
288 {
289         for (int k = 0; k < 8; ++k)
290                 frame.sunlight[k] = false;
291         for (int k = 0; k < 8; ++k) {
292                 LightPair light(getSmoothLightTransparent(blockpos_nodes + p, light_dirs[k], data));
293                 frame.lightsDay[k] = light.lightDay;
294                 frame.lightsNight[k] = light.lightNight;
295                 // If there is direct sunlight and no ambient occlusion at some corner,
296                 // mark the vertical edge (top and bottom corners) containing it.
297                 if (light.lightDay == 255) {
298                         frame.sunlight[k] = true;
299                         frame.sunlight[k ^ 2] = true;
300                 }
301         }
302 }
303
304 // Calculates vertex light level
305 //  vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
306 LightInfo MapblockMeshGenerator::blendLight(const v3f &vertex_pos)
307 {
308         // Light levels at (logical) node corners are known. Here,
309         // trilinear interpolation is used to calculate light level
310         // at a given point in the node.
311         f32 x = core::clamp(vertex_pos.X / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
312         f32 y = core::clamp(vertex_pos.Y / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
313         f32 z = core::clamp(vertex_pos.Z / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
314         f32 lightDay = 0.0; // daylight
315         f32 lightNight = 0.0;
316         f32 lightBoosted = 0.0; // daylight + direct sunlight, if any
317         for (int k = 0; k < 8; ++k) {
318                 f32 dx = (k & 4) ? x : 1 - x;
319                 f32 dy = (k & 2) ? y : 1 - y;
320                 f32 dz = (k & 1) ? z : 1 - z;
321                 // Use direct sunlight (255), if any; use daylight otherwise.
322                 f32 light_boosted = frame.sunlight[k] ? 255 : frame.lightsDay[k];
323                 lightDay += dx * dy * dz * frame.lightsDay[k];
324                 lightNight += dx * dy * dz * frame.lightsNight[k];
325                 lightBoosted += dx * dy * dz * light_boosted;
326         }
327         return LightInfo{lightDay, lightNight, lightBoosted};
328 }
329
330 // Calculates vertex color to be used in mapblock mesh
331 //  vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
332 //  tile_color - node's tile color
333 video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos)
334 {
335         LightInfo light = blendLight(vertex_pos);
336         return encode_light(light.getPair(), f->light_source);
337 }
338
339 video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos,
340         const v3f &vertex_normal)
341 {
342         LightInfo light = blendLight(vertex_pos);
343         video::SColor color = encode_light(light.getPair(MYMAX(0.0f, vertex_normal.Y)), f->light_source);
344         if (!f->light_source)
345                 applyFacesShading(color, vertex_normal);
346         return color;
347 }
348
349 void MapblockMeshGenerator::generateCuboidTextureCoords(const aabb3f &box, f32 *coords)
350 {
351         f32 tx1 = (box.MinEdge.X / BS) + 0.5;
352         f32 ty1 = (box.MinEdge.Y / BS) + 0.5;
353         f32 tz1 = (box.MinEdge.Z / BS) + 0.5;
354         f32 tx2 = (box.MaxEdge.X / BS) + 0.5;
355         f32 ty2 = (box.MaxEdge.Y / BS) + 0.5;
356         f32 tz2 = (box.MaxEdge.Z / BS) + 0.5;
357         f32 txc[24] = {
358                     tx1, 1 - tz2,     tx2, 1 - tz1, // up
359                     tx1,     tz1,     tx2,     tz2, // down
360                     tz1, 1 - ty2,     tz2, 1 - ty1, // right
361                 1 - tz2, 1 - ty2, 1 - tz1, 1 - ty1, // left
362                 1 - tx2, 1 - ty2, 1 - tx1, 1 - ty1, // back
363                     tx1, 1 - ty2,     tx2, 1 - ty1, // front
364         };
365         for (int i = 0; i != 24; ++i)
366                 coords[i] = txc[i];
367 }
368
369 void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc,
370         TileSpec *tiles, int tile_count, u8 mask)
371 {
372         bool scale = std::fabs(f->visual_scale - 1.0f) > 1e-3f;
373         f32 texture_coord_buf[24];
374         f32 dx1 = box.MinEdge.X;
375         f32 dy1 = box.MinEdge.Y;
376         f32 dz1 = box.MinEdge.Z;
377         f32 dx2 = box.MaxEdge.X;
378         f32 dy2 = box.MaxEdge.Y;
379         f32 dz2 = box.MaxEdge.Z;
380         if (scale) {
381                 if (!txc) { // generate texture coords before scaling
382                         generateCuboidTextureCoords(box, texture_coord_buf);
383                         txc = texture_coord_buf;
384                 }
385                 box.MinEdge *= f->visual_scale;
386                 box.MaxEdge *= f->visual_scale;
387         }
388         box.MinEdge += origin;
389         box.MaxEdge += origin;
390         if (!txc) {
391                 generateCuboidTextureCoords(box, texture_coord_buf);
392                 txc = texture_coord_buf;
393         }
394         if (!tiles) {
395                 tiles = &tile;
396                 tile_count = 1;
397         }
398         if (data->m_smooth_lighting) {
399                 LightInfo lights[8];
400                 for (int j = 0; j < 8; ++j) {
401                         v3f d;
402                         d.X = (j & 4) ? dx2 : dx1;
403                         d.Y = (j & 2) ? dy2 : dy1;
404                         d.Z = (j & 1) ? dz2 : dz1;
405                         lights[j] = blendLight(d);
406                 }
407                 drawCuboid(box, tiles, tile_count, lights, txc, mask);
408         } else {
409                 drawCuboid(box, tiles, tile_count, nullptr, txc, mask);
410         }
411 }
412
413 u8 MapblockMeshGenerator::getNodeBoxMask(aabb3f box, u8 solid_neighbors, u8 sametype_neighbors) const
414 {
415         const f32 NODE_BOUNDARY = 0.5 * BS;
416
417         // For an oversized nodebox, return immediately
418         if (box.MaxEdge.X > NODE_BOUNDARY ||
419                         box.MinEdge.X < -NODE_BOUNDARY ||
420                         box.MaxEdge.Y >  NODE_BOUNDARY ||
421                         box.MinEdge.Y < -NODE_BOUNDARY ||
422                         box.MaxEdge.Z >  NODE_BOUNDARY ||
423                         box.MinEdge.Z < -NODE_BOUNDARY)
424                 return 0;
425
426         // We can skip faces at node boundary if the matching neighbor is solid
427         u8 solid_mask =
428                         (box.MaxEdge.Y == NODE_BOUNDARY  ?  1 : 0) |
429                         (box.MinEdge.Y == -NODE_BOUNDARY ?  2 : 0) |
430                         (box.MaxEdge.X == NODE_BOUNDARY  ?  4 : 0) |
431                         (box.MinEdge.X == -NODE_BOUNDARY ?  8 : 0) |
432                         (box.MaxEdge.Z == NODE_BOUNDARY  ? 16 : 0) |
433                         (box.MinEdge.Z == -NODE_BOUNDARY ? 32 : 0);
434
435         u8 sametype_mask = 0;
436         if (f->alpha == AlphaMode::ALPHAMODE_OPAQUE) {
437                 // In opaque nodeboxes, faces on opposite sides can cancel
438                 // each other out if there is a matching neighbor of the same type
439                 sametype_mask =
440                                 ((solid_mask & 3) == 3 ? 3 : 0) |
441                                 ((solid_mask & 12) == 12 ? 12 : 0) |
442                                 ((solid_mask & 48) == 48 ? 48 : 0);
443         }
444
445         // Combine masks with actual neighbors to get the faces to be skipped
446         return (solid_mask & solid_neighbors) | (sametype_mask & sametype_neighbors);
447 }
448
449
450 void MapblockMeshGenerator::prepareLiquidNodeDrawing()
451 {
452         getSpecialTile(0, &tile_liquid_top);
453         getSpecialTile(1, &tile_liquid);
454
455         MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y + 1, p.Z));
456         MapNode nbottom = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y - 1, p.Z));
457         c_flowing = f->liquid_alternative_flowing_id;
458         c_source = f->liquid_alternative_source_id;
459         top_is_same_liquid = (ntop.getContent() == c_flowing) || (ntop.getContent() == c_source);
460         draw_liquid_bottom = (nbottom.getContent() != c_flowing) && (nbottom.getContent() != c_source);
461         if (draw_liquid_bottom) {
462                 const ContentFeatures &f2 = nodedef->get(nbottom.getContent());
463                 if (f2.solidness > 1)
464                         draw_liquid_bottom = false;
465         }
466
467         if (data->m_smooth_lighting)
468                 return; // don't need to pre-compute anything in this case
469
470         if (f->light_source != 0) {
471                 // If this liquid emits light and doesn't contain light, draw
472                 // it at what it emits, for an increased effect
473                 u8 e = decode_light(f->light_source);
474                 light = LightPair(std::max(e, light.lightDay), std::max(e, light.lightNight));
475         } else if (nodedef->getLightingFlags(ntop).has_light) {
476                 // Otherwise, use the light of the node on top if possible
477                 light = LightPair(getInteriorLight(ntop, 0, nodedef));
478         }
479
480         color_liquid_top = encode_light(light, f->light_source);
481         color = encode_light(light, f->light_source);
482 }
483
484 void MapblockMeshGenerator::getLiquidNeighborhood()
485 {
486         u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8);
487
488         for (int w = -1; w <= 1; w++)
489         for (int u = -1; u <= 1; u++) {
490                 NeighborData &neighbor = liquid_neighbors[w + 1][u + 1];
491                 v3s16 p2 = p + v3s16(u, 0, w);
492                 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
493                 neighbor.content = n2.getContent();
494                 neighbor.level = -0.5 * BS;
495                 neighbor.is_same_liquid = false;
496                 neighbor.top_is_same_liquid = false;
497
498                 if (neighbor.content == CONTENT_IGNORE)
499                         continue;
500
501                 if (neighbor.content == c_source) {
502                         neighbor.is_same_liquid = true;
503                         neighbor.level = 0.5 * BS;
504                 } else if (neighbor.content == c_flowing) {
505                         neighbor.is_same_liquid = true;
506                         u8 liquid_level = (n2.param2 & LIQUID_LEVEL_MASK);
507                         if (liquid_level <= LIQUID_LEVEL_MAX + 1 - range)
508                                 liquid_level = 0;
509                         else
510                                 liquid_level -= (LIQUID_LEVEL_MAX + 1 - range);
511                         neighbor.level = (-0.5 + (liquid_level + 0.5) / range) * BS;
512                 }
513
514                 // Check node above neighbor.
515                 // NOTE: This doesn't get executed if neighbor
516                 //       doesn't exist
517                 p2.Y++;
518                 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
519                 if (n2.getContent() == c_source || n2.getContent() == c_flowing)
520                         neighbor.top_is_same_liquid = true;
521         }
522 }
523
524 void MapblockMeshGenerator::calculateCornerLevels()
525 {
526         for (int k = 0; k < 2; k++)
527         for (int i = 0; i < 2; i++)
528                 corner_levels[k][i] = getCornerLevel(i, k);
529 }
530
531 f32 MapblockMeshGenerator::getCornerLevel(int i, int k)
532 {
533         float sum = 0;
534         int count = 0;
535         int air_count = 0;
536         for (int dk = 0; dk < 2; dk++)
537         for (int di = 0; di < 2; di++) {
538                 NeighborData &neighbor_data = liquid_neighbors[k + dk][i + di];
539                 content_t content = neighbor_data.content;
540
541                 // If top is liquid, draw starting from top of node
542                 if (neighbor_data.top_is_same_liquid)
543                         return 0.5 * BS;
544
545                 // Source always has the full height
546                 if (content == c_source)
547                         return 0.5 * BS;
548
549                 // Flowing liquid has level information
550                 if (content == c_flowing) {
551                         sum += neighbor_data.level;
552                         count++;
553                 } else if (content == CONTENT_AIR) {
554                         air_count++;
555                 }
556         }
557         if (air_count >= 2)
558                 return -0.5 * BS + 0.2;
559         if (count > 0)
560                 return sum / count;
561         return 0;
562 }
563
564 namespace {
565         struct LiquidFaceDesc {
566                 v3s16 dir; // XZ
567                 v3s16 p[2]; // XZ only; 1 means +, 0 means -
568         };
569         struct UV {
570                 int u, v;
571         };
572         static const LiquidFaceDesc liquid_base_faces[4] = {
573                 {v3s16( 1, 0,  0), {v3s16(1, 0, 1), v3s16(1, 0, 0)}},
574                 {v3s16(-1, 0,  0), {v3s16(0, 0, 0), v3s16(0, 0, 1)}},
575                 {v3s16( 0, 0,  1), {v3s16(0, 0, 1), v3s16(1, 0, 1)}},
576                 {v3s16( 0, 0, -1), {v3s16(1, 0, 0), v3s16(0, 0, 0)}},
577         };
578         static const UV liquid_base_vertices[4] = {
579                 {0, 1},
580                 {1, 1},
581                 {1, 0},
582                 {0, 0}
583         };
584 }
585
586 void MapblockMeshGenerator::drawLiquidSides()
587 {
588         for (const auto &face : liquid_base_faces) {
589                 const NeighborData &neighbor = liquid_neighbors[face.dir.Z + 1][face.dir.X + 1];
590
591                 // No face between nodes of the same liquid, unless there is node
592                 // at the top to which it should be connected. Again, unless the face
593                 // there would be inside the liquid
594                 if (neighbor.is_same_liquid) {
595                         if (!top_is_same_liquid)
596                                 continue;
597                         if (neighbor.top_is_same_liquid)
598                                 continue;
599                 }
600
601                 const ContentFeatures &neighbor_features = nodedef->get(neighbor.content);
602                 // Don't draw face if neighbor is blocking the view
603                 if (neighbor_features.solidness == 2)
604                         continue;
605
606                 video::S3DVertex vertices[4];
607                 for (int j = 0; j < 4; j++) {
608                         const UV &vertex = liquid_base_vertices[j];
609                         const v3s16 &base = face.p[vertex.u];
610                         float v = vertex.v;
611
612                         v3f pos;
613                         pos.X = (base.X - 0.5f) * BS;
614                         pos.Z = (base.Z - 0.5f) * BS;
615                         if (vertex.v) {
616                                 pos.Y = neighbor.is_same_liquid ? corner_levels[base.Z][base.X] : -0.5f * BS;
617                         } else if (top_is_same_liquid) {
618                                 pos.Y = 0.5f * BS;
619                         } else {
620                                 pos.Y = corner_levels[base.Z][base.X];
621                                 v += (0.5f * BS - corner_levels[base.Z][base.X]) / BS;
622                         }
623
624                         if (data->m_smooth_lighting)
625                                 color = blendLightColor(pos);
626                         pos += origin;
627                         vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, color, vertex.u, v);
628                 };
629                 collector->append(tile_liquid, vertices, 4, quad_indices, 6);
630         }
631 }
632
633 void MapblockMeshGenerator::drawLiquidTop()
634 {
635         // To get backface culling right, the vertices need to go
636         // clockwise around the front of the face. And we happened to
637         // calculate corner levels in exact reverse order.
638         static const int corner_resolve[4][2] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
639
640         video::S3DVertex vertices[4] = {
641                 video::S3DVertex(-BS / 2, 0,  BS / 2, 0, 0, 0, color_liquid_top, 0, 1),
642                 video::S3DVertex( BS / 2, 0,  BS / 2, 0, 0, 0, color_liquid_top, 1, 1),
643                 video::S3DVertex( BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 1, 0),
644                 video::S3DVertex(-BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 0, 0),
645         };
646
647         for (int i = 0; i < 4; i++) {
648                 int u = corner_resolve[i][0];
649                 int w = corner_resolve[i][1];
650                 vertices[i].Pos.Y += corner_levels[w][u];
651                 if (data->m_smooth_lighting)
652                         vertices[i].Color = blendLightColor(vertices[i].Pos);
653                 vertices[i].Pos += origin;
654         }
655
656         // Default downwards-flowing texture animation goes from
657         // -Z towards +Z, thus the direction is +Z.
658         // Rotate texture to make animation go in flow direction
659         // Positive if liquid moves towards +Z
660         f32 dz = (corner_levels[0][0] + corner_levels[0][1]) -
661                  (corner_levels[1][0] + corner_levels[1][1]);
662         // Positive if liquid moves towards +X
663         f32 dx = (corner_levels[0][0] + corner_levels[1][0]) -
664                  (corner_levels[0][1] + corner_levels[1][1]);
665         f32 tcoord_angle = atan2(dz, dx) * core::RADTODEG;
666         v2f tcoord_center(0.5, 0.5);
667         v2f tcoord_translate(blockpos_nodes.Z + p.Z, blockpos_nodes.X + p.X);
668         tcoord_translate.rotateBy(tcoord_angle);
669         tcoord_translate.X -= floor(tcoord_translate.X);
670         tcoord_translate.Y -= floor(tcoord_translate.Y);
671
672         for (video::S3DVertex &vertex : vertices) {
673                 vertex.TCoords.rotateBy(tcoord_angle, tcoord_center);
674                 vertex.TCoords += tcoord_translate;
675         }
676
677         std::swap(vertices[0].TCoords, vertices[2].TCoords);
678
679         collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
680 }
681
682 void MapblockMeshGenerator::drawLiquidBottom()
683 {
684         video::S3DVertex vertices[4] = {
685                 video::S3DVertex(-BS / 2, -BS / 2, -BS / 2, 0, 0, 0, color_liquid_top, 0, 0),
686                 video::S3DVertex( BS / 2, -BS / 2, -BS / 2, 0, 0, 0, color_liquid_top, 1, 0),
687                 video::S3DVertex( BS / 2, -BS / 2,  BS / 2, 0, 0, 0, color_liquid_top, 1, 1),
688                 video::S3DVertex(-BS / 2, -BS / 2,  BS / 2, 0, 0, 0, color_liquid_top, 0, 1),
689         };
690
691         for (int i = 0; i < 4; i++) {
692                 if (data->m_smooth_lighting)
693                         vertices[i].Color = blendLightColor(vertices[i].Pos);
694                 vertices[i].Pos += origin;
695         }
696
697         collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
698 }
699
700 void MapblockMeshGenerator::drawLiquidNode()
701 {
702         prepareLiquidNodeDrawing();
703         getLiquidNeighborhood();
704         calculateCornerLevels();
705         drawLiquidSides();
706         if (!top_is_same_liquid)
707                 drawLiquidTop();
708         if (draw_liquid_bottom)
709                 drawLiquidBottom();
710 }
711
712 void MapblockMeshGenerator::drawGlasslikeNode()
713 {
714         useTile(0, 0, 0);
715
716         for (int face = 0; face < 6; face++) {
717                 // Check this neighbor
718                 v3s16 dir = g_6dirs[face];
719                 v3s16 neighbor_pos = blockpos_nodes + p + dir;
720                 MapNode neighbor = data->m_vmanip.getNodeNoExNoEmerge(neighbor_pos);
721                 // Don't make face if neighbor is of same type
722                 if (neighbor.getContent() == n.getContent())
723                         continue;
724                 // Face at Z-
725                 v3f vertices[4] = {
726                         v3f(-BS / 2,  BS / 2, -BS / 2),
727                         v3f( BS / 2,  BS / 2, -BS / 2),
728                         v3f( BS / 2, -BS / 2, -BS / 2),
729                         v3f(-BS / 2, -BS / 2, -BS / 2),
730                 };
731
732                 for (v3f &vertex : vertices) {
733                         switch (face) {
734                                 case D6D_ZP:
735                                         vertex.rotateXZBy(180); break;
736                                 case D6D_YP:
737                                         vertex.rotateYZBy( 90); break;
738                                 case D6D_XP:
739                                         vertex.rotateXZBy( 90); break;
740                                 case D6D_ZN:
741                                         vertex.rotateXZBy(  0); break;
742                                 case D6D_YN:
743                                         vertex.rotateYZBy(-90); break;
744                                 case D6D_XN:
745                                         vertex.rotateXZBy(-90); break;
746                         }
747                 }
748                 drawQuad(vertices, dir);
749         }
750 }
751
752 void MapblockMeshGenerator::drawGlasslikeFramedNode()
753 {
754         TileSpec tiles[6];
755         for (int face = 0; face < 6; face++)
756                 getTile(g_6dirs[face], &tiles[face]);
757
758         if (!data->m_smooth_lighting)
759                 color = encode_light(light, f->light_source);
760
761         TileSpec glass_tiles[6];
762         for (auto &glass_tile : glass_tiles)
763                 glass_tile = tiles[4];
764
765         // Only respect H/V merge bits when paramtype2 = "glasslikeliquidlevel" (liquid tank)
766         u8 param2 = (f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL) ? n.getParam2() : 0;
767         bool H_merge = !(param2 & 128);
768         bool V_merge = !(param2 & 64);
769         param2 &= 63;
770
771         static const float a = BS / 2.0f;
772         static const float g = a - 0.03f;
773         static const float b = 0.876f * (BS / 2.0f);
774
775         static const aabb3f frame_edges[FRAMED_EDGE_COUNT] = {
776                 aabb3f( b,  b, -a,  a,  a,  a), // y+
777                 aabb3f(-a,  b, -a, -b,  a,  a), // y+
778                 aabb3f( b, -a, -a,  a, -b,  a), // y-
779                 aabb3f(-a, -a, -a, -b, -b,  a), // y-
780                 aabb3f( b, -a,  b,  a,  a,  a), // x+
781                 aabb3f( b, -a, -a,  a,  a, -b), // x+
782                 aabb3f(-a, -a,  b, -b,  a,  a), // x-
783                 aabb3f(-a, -a, -a, -b,  a, -b), // x-
784                 aabb3f(-a,  b,  b,  a,  a,  a), // z+
785                 aabb3f(-a, -a,  b,  a, -b,  a), // z+
786                 aabb3f(-a, -a, -a,  a, -b, -b), // z-
787                 aabb3f(-a,  b, -a,  a,  a, -b), // z-
788         };
789
790         // tables of neighbor (connect if same type and merge allowed),
791         // checked with g_26dirs
792
793         // 1 = connect, 0 = face visible
794         bool nb[FRAMED_NEIGHBOR_COUNT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
795
796         // 1 = check
797         static const bool check_nb_vertical [FRAMED_NEIGHBOR_COUNT] =
798                 {0,1,0,0,1,0, 0,0,0,0, 0,0,0,0, 0,0,0,0};
799         static const bool check_nb_horizontal [FRAMED_NEIGHBOR_COUNT] =
800                 {1,0,1,1,0,1, 0,0,0,0, 1,1,1,1, 0,0,0,0};
801         static const bool check_nb_all [FRAMED_NEIGHBOR_COUNT] =
802                 {1,1,1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1};
803         const bool *check_nb = check_nb_all;
804
805         // neighbors checks for frames visibility
806         if (H_merge || V_merge) {
807                 if (!H_merge)
808                         check_nb = check_nb_vertical; // vertical-only merge
809                 if (!V_merge)
810                         check_nb = check_nb_horizontal; // horizontal-only merge
811                 content_t current = n.getContent();
812                 for (int i = 0; i < FRAMED_NEIGHBOR_COUNT; i++) {
813                         if (!check_nb[i])
814                                 continue;
815                         v3s16 n2p = blockpos_nodes + p + g_26dirs[i];
816                         MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
817                         content_t n2c = n2.getContent();
818                         if (n2c == current)
819                                 nb[i] = 1;
820                 }
821         }
822
823         // edge visibility
824
825         static const u8 nb_triplet[FRAMED_EDGE_COUNT][3] = {
826                 {1, 2,  7}, {1, 5,  6}, {4, 2, 15}, {4, 5, 14},
827                 {2, 0, 11}, {2, 3, 13}, {5, 0, 10}, {5, 3, 12},
828                 {0, 1,  8}, {0, 4, 16}, {3, 4, 17}, {3, 1,  9},
829         };
830
831         tile = tiles[1];
832         for (int edge = 0; edge < FRAMED_EDGE_COUNT; edge++) {
833                 bool edge_invisible;
834                 if (nb[nb_triplet[edge][2]])
835                         edge_invisible = nb[nb_triplet[edge][0]] & nb[nb_triplet[edge][1]];
836                 else
837                         edge_invisible = nb[nb_triplet[edge][0]] ^ nb[nb_triplet[edge][1]];
838                 if (edge_invisible)
839                         continue;
840                 drawAutoLightedCuboid(frame_edges[edge]);
841         }
842
843         for (int face = 0; face < 6; face++) {
844                 if (nb[face])
845                         continue;
846
847                 tile = glass_tiles[face];
848                 // Face at Z-
849                 v3f vertices[4] = {
850                         v3f(-a,  a, -g),
851                         v3f( a,  a, -g),
852                         v3f( a, -a, -g),
853                         v3f(-a, -a, -g),
854                 };
855
856                 for (v3f &vertex : vertices) {
857                         switch (face) {
858                                 case D6D_ZP:
859                                         vertex.rotateXZBy(180); break;
860                                 case D6D_YP:
861                                         vertex.rotateYZBy( 90); break;
862                                 case D6D_XP:
863                                         vertex.rotateXZBy( 90); break;
864                                 case D6D_ZN:
865                                         vertex.rotateXZBy(  0); break;
866                                 case D6D_YN:
867                                         vertex.rotateYZBy(-90); break;
868                                 case D6D_XN:
869                                         vertex.rotateXZBy(-90); break;
870                         }
871                 }
872                 v3s16 dir = g_6dirs[face];
873                 drawQuad(vertices, dir);
874         }
875
876         // Optionally render internal liquid level defined by param2
877         // Liquid is textured with 1 tile defined in nodedef 'special_tiles'
878         if (param2 > 0 && f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL &&
879                         f->special_tiles[0].layers[0].texture) {
880                 // Internal liquid level has param2 range 0 .. 63,
881                 // convert it to -0.5 .. 0.5
882                 float vlev = (param2 / 63.0f) * 2.0f - 1.0f;
883                 getSpecialTile(0, &tile);
884                 drawAutoLightedCuboid(aabb3f(-(nb[5] ? g : b),
885                                              -(nb[4] ? g : b),
886                                              -(nb[3] ? g : b),
887                                               (nb[2] ? g : b),
888                                               (nb[1] ? g : b) * vlev,
889                                               (nb[0] ? g : b)));
890         }
891 }
892
893 void MapblockMeshGenerator::drawAllfacesNode()
894 {
895         static const aabb3f box(-BS / 2, -BS / 2, -BS / 2, BS / 2, BS / 2, BS / 2);
896         useTile(0, 0, 0);
897         drawAutoLightedCuboid(box);
898 }
899
900 void MapblockMeshGenerator::drawTorchlikeNode()
901 {
902         u8 wall = n.getWallMounted(nodedef);
903         u8 tileindex = 0;
904         switch (wall) {
905                 case DWM_YP: tileindex = 1; break; // ceiling
906                 case DWM_YN: tileindex = 0; break; // floor
907                 default:     tileindex = 2; // side (or invalid—should we care?)
908         }
909         useTile(tileindex, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
910
911         float size = BS / 2 * f->visual_scale;
912         v3f vertices[4] = {
913                 v3f(-size,  size, 0),
914                 v3f( size,  size, 0),
915                 v3f( size, -size, 0),
916                 v3f(-size, -size, 0),
917         };
918
919         for (v3f &vertex : vertices) {
920                 switch (wall) {
921                         case DWM_YP:
922                                 vertex.Y += -size + BS/2;
923                                 vertex.rotateXZBy(-45);
924                                 break;
925                         case DWM_YN:
926                                 vertex.Y += size - BS/2;
927                                 vertex.rotateXZBy(45);
928                                 break;
929                         case DWM_XP:
930                                 vertex.X += -size + BS/2;
931                                 break;
932                         case DWM_XN:
933                                 vertex.X += -size + BS/2;
934                                 vertex.rotateXZBy(180);
935                                 break;
936                         case DWM_ZP:
937                                 vertex.X += -size + BS/2;
938                                 vertex.rotateXZBy(90);
939                                 break;
940                         case DWM_ZN:
941                                 vertex.X += -size + BS/2;
942                                 vertex.rotateXZBy(-90);
943                 }
944         }
945         drawQuad(vertices);
946 }
947
948 void MapblockMeshGenerator::drawSignlikeNode()
949 {
950         u8 wall = n.getWallMounted(nodedef);
951         useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
952         static const float offset = BS / 16;
953         float size = BS / 2 * f->visual_scale;
954         // Wall at X+ of node
955         v3f vertices[4] = {
956                 v3f(BS / 2 - offset,  size,  size),
957                 v3f(BS / 2 - offset,  size, -size),
958                 v3f(BS / 2 - offset, -size, -size),
959                 v3f(BS / 2 - offset, -size,  size),
960         };
961
962         for (v3f &vertex : vertices) {
963                 switch (wall) {
964                         case DWM_YP:
965                                 vertex.rotateXYBy( 90); break;
966                         case DWM_YN:
967                                 vertex.rotateXYBy(-90); break;
968                         case DWM_XP:
969                                 vertex.rotateXZBy(  0); break;
970                         case DWM_XN:
971                                 vertex.rotateXZBy(180); break;
972                         case DWM_ZP:
973                                 vertex.rotateXZBy( 90); break;
974                         case DWM_ZN:
975                                 vertex.rotateXZBy(-90); break;
976                 }
977         }
978         drawQuad(vertices);
979 }
980
981 void MapblockMeshGenerator::drawPlantlikeQuad(float rotation, float quad_offset,
982         bool offset_top_only)
983 {
984         v3f vertices[4] = {
985                 v3f(-scale, -BS / 2 + 2.0 * scale * plant_height, 0),
986                 v3f( scale, -BS / 2 + 2.0 * scale * plant_height, 0),
987                 v3f( scale, -BS / 2, 0),
988                 v3f(-scale, -BS / 2, 0),
989         };
990         if (random_offset_Y) {
991                 PseudoRandom yrng(face_num++ | p.X << 16 | p.Z << 8 | p.Y << 24);
992                 offset.Y = -BS * ((yrng.next() % 16 / 16.0) * 0.125);
993         }
994         int offset_count = offset_top_only ? 2 : 4;
995         for (int i = 0; i < offset_count; i++)
996                 vertices[i].Z += quad_offset;
997
998         for (v3f &vertex : vertices) {
999                 vertex.rotateXZBy(rotation + rotate_degree);
1000                 vertex += offset;
1001         }
1002
1003         u8 wall = n.getWallMounted(nodedef);
1004         if (wall != DWM_YN) {
1005                 for (v3f &vertex : vertices) {
1006                         switch (wall) {
1007                                 case DWM_YP:
1008                                         vertex.rotateYZBy(180);
1009                                         vertex.rotateXZBy(180);
1010                                         break;
1011                                 case DWM_XP:
1012                                         vertex.rotateXYBy(90);
1013                                         break;
1014                                 case DWM_XN:
1015                                         vertex.rotateXYBy(-90);
1016                                         vertex.rotateYZBy(180);
1017                                         break;
1018                                 case DWM_ZP:
1019                                         vertex.rotateYZBy(-90);
1020                                         vertex.rotateXYBy(90);
1021                                         break;
1022                                 case DWM_ZN:
1023                                         vertex.rotateYZBy(90);
1024                                         vertex.rotateXYBy(90);
1025                                         break;
1026                         }
1027                 }
1028         }
1029
1030         drawQuad(vertices, v3s16(0, 0, 0), plant_height);
1031 }
1032
1033 void MapblockMeshGenerator::drawPlantlike(bool is_rooted)
1034 {
1035         draw_style = PLANT_STYLE_CROSS;
1036         scale = BS / 2 * f->visual_scale;
1037         offset = v3f(0, 0, 0);
1038         rotate_degree = 0.0f;
1039         random_offset_Y = false;
1040         face_num = 0;
1041         plant_height = 1.0;
1042
1043         switch (f->param_type_2) {
1044         case CPT2_MESHOPTIONS:
1045                 draw_style = PlantlikeStyle(n.param2 & MO_MASK_STYLE);
1046                 if (n.param2 & MO_BIT_SCALE_SQRT2)
1047                         scale *= 1.41421;
1048                 if (n.param2 & MO_BIT_RANDOM_OFFSET) {
1049                         PseudoRandom rng(p.X << 8 | p.Z | p.Y << 16);
1050                         offset.X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
1051                         offset.Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
1052                 }
1053                 if (n.param2 & MO_BIT_RANDOM_OFFSET_Y)
1054                         random_offset_Y = true;
1055                 break;
1056
1057         case CPT2_DEGROTATE:
1058         case CPT2_COLORED_DEGROTATE:
1059                 rotate_degree = 1.5f * n.getDegRotate(nodedef);
1060                 break;
1061
1062         case CPT2_LEVELED:
1063                 plant_height = n.param2 / 16.0;
1064                 break;
1065
1066         default:
1067                 break;
1068         }
1069
1070         if (is_rooted) {
1071                 u8 wall = n.getWallMounted(nodedef);
1072                 switch (wall) {
1073                         case DWM_YP:
1074                                 offset.Y += BS*2;
1075                                 break;
1076                         case DWM_XN:
1077                         case DWM_XP:
1078                         case DWM_ZN:
1079                         case DWM_ZP:
1080                                 offset.X += -BS;
1081                                 offset.Y +=  BS;
1082                                 break;
1083                 }
1084         }
1085
1086         switch (draw_style) {
1087         case PLANT_STYLE_CROSS:
1088                 drawPlantlikeQuad(46);
1089                 drawPlantlikeQuad(-44);
1090                 break;
1091
1092         case PLANT_STYLE_CROSS2:
1093                 drawPlantlikeQuad(91);
1094                 drawPlantlikeQuad(1);
1095                 break;
1096
1097         case PLANT_STYLE_STAR:
1098                 drawPlantlikeQuad(121);
1099                 drawPlantlikeQuad(241);
1100                 drawPlantlikeQuad(1);
1101                 break;
1102
1103         case PLANT_STYLE_HASH:
1104                 drawPlantlikeQuad(  1, BS / 4);
1105                 drawPlantlikeQuad( 91, BS / 4);
1106                 drawPlantlikeQuad(181, BS / 4);
1107                 drawPlantlikeQuad(271, BS / 4);
1108                 break;
1109
1110         case PLANT_STYLE_HASH2:
1111                 drawPlantlikeQuad(  1, -BS / 2, true);
1112                 drawPlantlikeQuad( 91, -BS / 2, true);
1113                 drawPlantlikeQuad(181, -BS / 2, true);
1114                 drawPlantlikeQuad(271, -BS / 2, true);
1115                 break;
1116         }
1117 }
1118
1119 void MapblockMeshGenerator::drawPlantlikeNode()
1120 {
1121         useTile();
1122         drawPlantlike();
1123 }
1124
1125 void MapblockMeshGenerator::drawPlantlikeRootedNode()
1126 {
1127         useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, 0, true);
1128         origin += v3f(0.0, BS, 0.0);
1129         p.Y++;
1130         if (data->m_smooth_lighting) {
1131                 getSmoothLightFrame();
1132         } else {
1133                 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1134                 light = LightPair(getInteriorLight(ntop, 0, nodedef));
1135         }
1136         drawPlantlike(true);
1137         p.Y--;
1138 }
1139
1140 void MapblockMeshGenerator::drawFirelikeQuad(float rotation, float opening_angle,
1141         float offset_h, float offset_v)
1142 {
1143         v3f vertices[4] = {
1144                 v3f(-scale, -BS / 2 + scale * 2, 0),
1145                 v3f( scale, -BS / 2 + scale * 2, 0),
1146                 v3f( scale, -BS / 2, 0),
1147                 v3f(-scale, -BS / 2, 0),
1148         };
1149
1150         for (v3f &vertex : vertices) {
1151                 vertex.rotateYZBy(opening_angle);
1152                 vertex.Z += offset_h;
1153                 vertex.rotateXZBy(rotation);
1154                 vertex.Y += offset_v;
1155         }
1156         drawQuad(vertices);
1157 }
1158
1159 void MapblockMeshGenerator::drawFirelikeNode()
1160 {
1161         useTile();
1162         scale = BS / 2 * f->visual_scale;
1163
1164         // Check for adjacent nodes
1165         bool neighbors = false;
1166         bool neighbor[6] = {0, 0, 0, 0, 0, 0};
1167         content_t current = n.getContent();
1168         for (int i = 0; i < 6; i++) {
1169                 v3s16 n2p = blockpos_nodes + p + g_6dirs[i];
1170                 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
1171                 content_t n2c = n2.getContent();
1172                 if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
1173                         neighbor[i] = true;
1174                         neighbors = true;
1175                 }
1176         }
1177         bool drawBasicFire = neighbor[D6D_YN] || !neighbors;
1178         bool drawBottomFire = neighbor[D6D_YP];
1179
1180         if (drawBasicFire || neighbor[D6D_ZP])
1181                 drawFirelikeQuad(0, -10, 0.4 * BS);
1182         else if (drawBottomFire)
1183                 drawFirelikeQuad(0, 70, 0.47 * BS, 0.484 * BS);
1184
1185         if (drawBasicFire || neighbor[D6D_XN])
1186                 drawFirelikeQuad(90, -10, 0.4 * BS);
1187         else if (drawBottomFire)
1188                 drawFirelikeQuad(90, 70, 0.47 * BS, 0.484 * BS);
1189
1190         if (drawBasicFire || neighbor[D6D_ZN])
1191                 drawFirelikeQuad(180, -10, 0.4 * BS);
1192         else if (drawBottomFire)
1193                 drawFirelikeQuad(180, 70, 0.47 * BS, 0.484 * BS);
1194
1195         if (drawBasicFire || neighbor[D6D_XP])
1196                 drawFirelikeQuad(270, -10, 0.4 * BS);
1197         else if (drawBottomFire)
1198                 drawFirelikeQuad(270, 70, 0.47 * BS, 0.484 * BS);
1199
1200         if (drawBasicFire) {
1201                 drawFirelikeQuad(45, 0, 0.0);
1202                 drawFirelikeQuad(-45, 0, 0.0);
1203         }
1204 }
1205
1206 void MapblockMeshGenerator::drawFencelikeNode()
1207 {
1208         useTile(0, 0, 0);
1209         TileSpec tile_nocrack = tile;
1210
1211         for (auto &layer : tile_nocrack.layers)
1212                 layer.material_flags &= ~MATERIAL_FLAG_CRACK;
1213
1214         // Put wood the right way around in the posts
1215         TileSpec tile_rot = tile;
1216         tile_rot.rotation = 1;
1217
1218         static const f32 post_rad = BS / 8;
1219         static const f32 bar_rad  = BS / 16;
1220         static const f32 bar_len  = BS / 2 - post_rad;
1221
1222         // The post - always present
1223         static const aabb3f post(-post_rad, -BS / 2, -post_rad,
1224                                   post_rad,  BS / 2,  post_rad);
1225         static const f32 postuv[24] = {
1226                 0.375, 0.375, 0.625, 0.625,
1227                 0.375, 0.375, 0.625, 0.625,
1228                 0.000, 0.000, 0.250, 1.000,
1229                 0.250, 0.000, 0.500, 1.000,
1230                 0.500, 0.000, 0.750, 1.000,
1231                 0.750, 0.000, 1.000, 1.000,
1232         };
1233         tile = tile_rot;
1234         drawAutoLightedCuboid(post, postuv);
1235
1236         tile = tile_nocrack;
1237
1238         // Now a section of fence, +X, if there's a post there
1239         v3s16 p2 = p;
1240         p2.X++;
1241         MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1242         const ContentFeatures *f2 = &nodedef->get(n2);
1243         if (f2->drawtype == NDT_FENCELIKE) {
1244                 static const aabb3f bar_x1(BS / 2 - bar_len,  BS / 4 - bar_rad, -bar_rad,
1245                                            BS / 2 + bar_len,  BS / 4 + bar_rad,  bar_rad);
1246                 static const aabb3f bar_x2(BS / 2 - bar_len, -BS / 4 - bar_rad, -bar_rad,
1247                                            BS / 2 + bar_len, -BS / 4 + bar_rad,  bar_rad);
1248                 static const f32 xrailuv[24] = {
1249                         0.000, 0.125, 1.000, 0.250,
1250                         0.000, 0.250, 1.000, 0.375,
1251                         0.375, 0.375, 0.500, 0.500,
1252                         0.625, 0.625, 0.750, 0.750,
1253                         0.000, 0.500, 1.000, 0.625,
1254                         0.000, 0.875, 1.000, 1.000,
1255                 };
1256                 drawAutoLightedCuboid(bar_x1, xrailuv);
1257                 drawAutoLightedCuboid(bar_x2, xrailuv);
1258         }
1259
1260         // Now a section of fence, +Z, if there's a post there
1261         p2 = p;
1262         p2.Z++;
1263         n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1264         f2 = &nodedef->get(n2);
1265         if (f2->drawtype == NDT_FENCELIKE) {
1266                 static const aabb3f bar_z1(-bar_rad,  BS / 4 - bar_rad, BS / 2 - bar_len,
1267                                             bar_rad,  BS / 4 + bar_rad, BS / 2 + bar_len);
1268                 static const aabb3f bar_z2(-bar_rad, -BS / 4 - bar_rad, BS / 2 - bar_len,
1269                                             bar_rad, -BS / 4 + bar_rad, BS / 2 + bar_len);
1270                 static const f32 zrailuv[24] = {
1271                         0.1875, 0.0625, 0.3125, 0.3125, // cannot rotate; stretch
1272                         0.2500, 0.0625, 0.3750, 0.3125, // for wood texture instead
1273                         0.0000, 0.5625, 1.0000, 0.6875,
1274                         0.0000, 0.3750, 1.0000, 0.5000,
1275                         0.3750, 0.3750, 0.5000, 0.5000,
1276                         0.6250, 0.6250, 0.7500, 0.7500,
1277                 };
1278                 drawAutoLightedCuboid(bar_z1, zrailuv);
1279                 drawAutoLightedCuboid(bar_z2, zrailuv);
1280         }
1281 }
1282
1283 bool MapblockMeshGenerator::isSameRail(v3s16 dir)
1284 {
1285         MapNode node2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
1286         if (node2.getContent() == n.getContent())
1287                 return true;
1288         const ContentFeatures &def2 = nodedef->get(node2);
1289         return ((def2.drawtype == NDT_RAILLIKE) &&
1290                 (def2.getGroup(raillike_groupname) == raillike_group));
1291 }
1292
1293 namespace {
1294         static const v3s16 rail_direction[4] = {
1295                 v3s16( 0, 0,  1),
1296                 v3s16( 0, 0, -1),
1297                 v3s16(-1, 0,  0),
1298                 v3s16( 1, 0,  0),
1299         };
1300         static const int rail_slope_angle[4] = {0, 180, 90, -90};
1301
1302         enum RailTile {
1303                 straight,
1304                 curved,
1305                 junction,
1306                 cross,
1307         };
1308         struct RailDesc {
1309                 int tile_index;
1310                 int angle;
1311         };
1312         static const RailDesc rail_kinds[16] = {
1313                                  // +x -x -z +z
1314                                  //-------------
1315                 {straight,   0}, //  .  .  .  .
1316                 {straight,   0}, //  .  .  . +Z
1317                 {straight,   0}, //  .  . -Z  .
1318                 {straight,   0}, //  .  . -Z +Z
1319                 {straight,  90}, //  . -X  .  .
1320                 {  curved, 180}, //  . -X  . +Z
1321                 {  curved, 270}, //  . -X -Z  .
1322                 {junction, 180}, //  . -X -Z +Z
1323                 {straight,  90}, // +X  .  .  .
1324                 {  curved,  90}, // +X  .  . +Z
1325                 {  curved,   0}, // +X  . -Z  .
1326                 {junction,   0}, // +X  . -Z +Z
1327                 {straight,  90}, // +X -X  .  .
1328                 {junction,  90}, // +X -X  . +Z
1329                 {junction, 270}, // +X -X -Z  .
1330                 {   cross,   0}, // +X -X -Z +Z
1331         };
1332 }
1333
1334 void MapblockMeshGenerator::drawRaillikeNode()
1335 {
1336         raillike_group = nodedef->get(n).getGroup(raillike_groupname);
1337
1338         int code = 0;
1339         int angle;
1340         int tile_index;
1341         bool sloped = false;
1342         for (int dir = 0; dir < 4; dir++) {
1343                 bool rail_above = isSameRail(rail_direction[dir] + v3s16(0, 1, 0));
1344                 if (rail_above) {
1345                         sloped = true;
1346                         angle = rail_slope_angle[dir];
1347                 }
1348                 if (rail_above ||
1349                                 isSameRail(rail_direction[dir]) ||
1350                                 isSameRail(rail_direction[dir] + v3s16(0, -1, 0)))
1351                         code |= 1 << dir;
1352         }
1353
1354         if (sloped) {
1355                 tile_index = straight;
1356         } else {
1357                 tile_index = rail_kinds[code].tile_index;
1358                 angle = rail_kinds[code].angle;
1359         }
1360
1361         useTile(tile_index, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
1362
1363         static const float offset = BS / 64;
1364         static const float size   = BS / 2;
1365         float y2 = sloped ? size : -size;
1366         v3f vertices[4] = {
1367                 v3f(-size,    y2 + offset,  size),
1368                 v3f( size,    y2 + offset,  size),
1369                 v3f( size, -size + offset, -size),
1370                 v3f(-size, -size + offset, -size),
1371         };
1372         if (angle)
1373                 for (v3f &vertex : vertices)
1374                         vertex.rotateXZBy(angle);
1375         drawQuad(vertices);
1376 }
1377
1378 namespace {
1379         static const v3s16 nodebox_tile_dirs[6] = {
1380                 v3s16(0, 1, 0),
1381                 v3s16(0, -1, 0),
1382                 v3s16(1, 0, 0),
1383                 v3s16(-1, 0, 0),
1384                 v3s16(0, 0, 1),
1385                 v3s16(0, 0, -1)
1386         };
1387
1388         // we have this order for some reason...
1389         static const v3s16 nodebox_connection_dirs[6] = {
1390                 v3s16( 0,  1,  0), // top
1391                 v3s16( 0, -1,  0), // bottom
1392                 v3s16( 0,  0, -1), // front
1393                 v3s16(-1,  0,  0), // left
1394                 v3s16( 0,  0,  1), // back
1395                 v3s16( 1,  0,  0), // right
1396         };
1397 }
1398
1399 void MapblockMeshGenerator::drawNodeboxNode()
1400 {
1401         TileSpec tiles[6];
1402         for (int face = 0; face < 6; face++) {
1403                 // Handles facedir rotation for textures
1404                 getTile(nodebox_tile_dirs[face], &tiles[face]);
1405         }
1406
1407         bool param2_is_rotation =
1408                         f->param_type_2 == CPT2_COLORED_FACEDIR ||
1409                         f->param_type_2 == CPT2_COLORED_WALLMOUNTED ||
1410                         f->param_type_2 == CPT2_FACEDIR ||
1411                         f->param_type_2 == CPT2_WALLMOUNTED;
1412
1413         bool param2_is_level =
1414                         f->param_type_2 == CPT2_LEVELED;
1415
1416         // locate possible neighboring nodes to connect to
1417         u8 neighbors_set = 0;
1418         u8 solid_neighbors = 0;
1419         u8 sametype_neighbors = 0;
1420         for (int dir = 0; dir != 6; dir++) {
1421                 u8 flag = 1 << dir;
1422                 v3s16 p2 = blockpos_nodes + p + nodebox_tile_dirs[dir];
1423                 MapNode n2 = data->m_vmanip.getNodeNoEx(p2);
1424
1425                 // mark neighbors that are the same node type
1426                 // and have the same rotation or higher level stored as param2
1427                 if (n2.param0 == n.param0 &&
1428                                 (!param2_is_rotation || n.param2 == n2.param2) &&
1429                                 (!param2_is_level || n.param2 <= n2.param2))
1430                         sametype_neighbors |= flag;
1431
1432                 // mark neighbors that are simple solid blocks
1433                 if (nodedef->get(n2).drawtype == NDT_NORMAL)
1434                         solid_neighbors |= flag;
1435
1436                 if (f->node_box.type == NODEBOX_CONNECTED) {
1437                         p2 = blockpos_nodes + p + nodebox_connection_dirs[dir];
1438                         n2 = data->m_vmanip.getNodeNoEx(p2);
1439                         if (nodedef->nodeboxConnects(n, n2, flag))
1440                                 neighbors_set |= flag;
1441                 }
1442         }
1443
1444         std::vector<aabb3f> boxes;
1445         n.getNodeBoxes(nodedef, &boxes, neighbors_set);
1446
1447         bool isTransparent = false;
1448
1449         for (const TileSpec &tile : tiles) {
1450                 if (tile.layers[0].isTransparent()) {
1451                         isTransparent = true;
1452                         break;
1453                 }
1454         }
1455
1456         if (isTransparent) {
1457                 std::vector<float> sections;
1458                 // Preallocate 8 default splits + Min&Max for each nodebox
1459                 sections.reserve(8 + 2 * boxes.size());
1460
1461                 for (int axis = 0; axis < 3; axis++) {
1462                         // identify sections
1463
1464                         if (axis == 0) {
1465                                 // Default split at node bounds, up to 3 nodes in each direction
1466                                 for (float s = -3.5f * BS; s < 4.0f * BS; s += 1.0f * BS)
1467                                         sections.push_back(s);
1468                         }
1469                         else {
1470                                 // Avoid readding the same 8 default splits for Y and Z
1471                                 sections.resize(8);
1472                         }
1473
1474                         // Add edges of existing node boxes, rounded to 1E-3
1475                         for (size_t i = 0; i < boxes.size(); i++) {
1476                                 sections.push_back(std::floor(boxes[i].MinEdge[axis] * 1E3) * 1E-3);
1477                                 sections.push_back(std::floor(boxes[i].MaxEdge[axis] * 1E3) * 1E-3);
1478                         }
1479
1480                         // split the boxes at recorded sections
1481                         // limit splits to avoid runaway crash if inner loop adds infinite splits
1482                         // due to e.g. precision problems.
1483                         // 100 is just an arbitrary, reasonably high number.
1484                         for (size_t i = 0; i < boxes.size() && i < 100; i++) {
1485                                 aabb3f *box = &boxes[i];
1486                                 for (float section : sections) {
1487                                         if (box->MinEdge[axis] < section && box->MaxEdge[axis] > section) {
1488                                                 aabb3f copy(*box);
1489                                                 copy.MinEdge[axis] = section;
1490                                                 box->MaxEdge[axis] = section;
1491                                                 boxes.push_back(copy);
1492                                                 box = &boxes[i]; // find new address of the box in case of reallocation
1493                                         }
1494                                 }
1495                         }
1496                 }
1497         }
1498
1499         for (auto &box : boxes) {
1500                 u8 mask = getNodeBoxMask(box, solid_neighbors, sametype_neighbors);
1501                 drawAutoLightedCuboid(box, nullptr, tiles, 6, mask);
1502         }
1503 }
1504
1505 void MapblockMeshGenerator::drawMeshNode()
1506 {
1507         u8 facedir = 0;
1508         scene::IMesh* mesh;
1509         bool private_mesh; // as a grab/drop pair is not thread-safe
1510         int degrotate = 0;
1511
1512         if (f->param_type_2 == CPT2_FACEDIR ||
1513                         f->param_type_2 == CPT2_COLORED_FACEDIR ||
1514                         f->param_type_2 == CPT2_4DIR ||
1515                         f->param_type_2 == CPT2_COLORED_4DIR) {
1516                 facedir = n.getFaceDir(nodedef);
1517         } else if (f->param_type_2 == CPT2_WALLMOUNTED ||
1518                         f->param_type_2 == CPT2_COLORED_WALLMOUNTED) {
1519                 // Convert wallmounted to 6dfacedir.
1520                 // When cache enabled, it is already converted.
1521                 facedir = n.getWallMounted(nodedef);
1522                 if (!enable_mesh_cache)
1523                         facedir = wallmounted_to_facedir[facedir];
1524         } else if (f->param_type_2 == CPT2_DEGROTATE ||
1525                         f->param_type_2 == CPT2_COLORED_DEGROTATE) {
1526                 degrotate = n.getDegRotate(nodedef);
1527         }
1528
1529         if (!data->m_smooth_lighting && f->mesh_ptr[facedir] && !degrotate) {
1530                 // use cached meshes
1531                 private_mesh = false;
1532                 mesh = f->mesh_ptr[facedir];
1533         } else if (f->mesh_ptr[0]) {
1534                 // no cache, clone and rotate mesh
1535                 private_mesh = true;
1536                 mesh = cloneMesh(f->mesh_ptr[0]);
1537                 if (facedir)
1538                         rotateMeshBy6dFacedir(mesh, facedir);
1539                 else if (degrotate)
1540                         rotateMeshXZby(mesh, 1.5f * degrotate);
1541                 recalculateBoundingBox(mesh);
1542                 meshmanip->recalculateNormals(mesh, true, false);
1543         } else
1544                 return;
1545
1546         int mesh_buffer_count = mesh->getMeshBufferCount();
1547         for (int j = 0; j < mesh_buffer_count; j++) {
1548                 useTile(j);
1549                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1550                 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1551                 int vertex_count = buf->getVertexCount();
1552
1553                 if (data->m_smooth_lighting) {
1554                         // Mesh is always private here. So the lighting is applied to each
1555                         // vertex right here.
1556                         for (int k = 0; k < vertex_count; k++) {
1557                                 video::S3DVertex &vertex = vertices[k];
1558                                 vertex.Color = blendLightColor(vertex.Pos, vertex.Normal);
1559                                 vertex.Pos += origin;
1560                         }
1561                         collector->append(tile, vertices, vertex_count,
1562                                 buf->getIndices(), buf->getIndexCount());
1563                 } else {
1564                         // Don't modify the mesh, it may not be private here.
1565                         // Instead, let the collector process colors, etc.
1566                         collector->append(tile, vertices, vertex_count,
1567                                 buf->getIndices(), buf->getIndexCount(), origin,
1568                                 color, f->light_source);
1569                 }
1570         }
1571         if (private_mesh)
1572                 mesh->drop();
1573 }
1574
1575 // also called when the drawtype is known but should have been pre-converted
1576 void MapblockMeshGenerator::errorUnknownDrawtype()
1577 {
1578         infostream << "Got drawtype " << f->drawtype << std::endl;
1579         FATAL_ERROR("Unknown drawtype");
1580 }
1581
1582 void MapblockMeshGenerator::drawNode()
1583 {
1584         // skip some drawtypes early
1585         switch (f->drawtype) {
1586                 case NDT_NORMAL:   // Drawn by MapBlockMesh
1587                 case NDT_AIRLIKE:  // Not drawn at all
1588                 case NDT_LIQUID:   // Drawn by MapBlockMesh
1589                         return;
1590                 default:
1591                         break;
1592         }
1593         origin = intToFloat(p, BS);
1594         if (data->m_smooth_lighting)
1595                 getSmoothLightFrame();
1596         else
1597                 light = LightPair(getInteriorLight(n, 0, nodedef));
1598         switch (f->drawtype) {
1599                 case NDT_FLOWINGLIQUID:     drawLiquidNode(); break;
1600                 case NDT_GLASSLIKE:         drawGlasslikeNode(); break;
1601                 case NDT_GLASSLIKE_FRAMED:  drawGlasslikeFramedNode(); break;
1602                 case NDT_ALLFACES:          drawAllfacesNode(); break;
1603                 case NDT_TORCHLIKE:         drawTorchlikeNode(); break;
1604                 case NDT_SIGNLIKE:          drawSignlikeNode(); break;
1605                 case NDT_PLANTLIKE:         drawPlantlikeNode(); break;
1606                 case NDT_PLANTLIKE_ROOTED:  drawPlantlikeRootedNode(); break;
1607                 case NDT_FIRELIKE:          drawFirelikeNode(); break;
1608                 case NDT_FENCELIKE:         drawFencelikeNode(); break;
1609                 case NDT_RAILLIKE:          drawRaillikeNode(); break;
1610                 case NDT_NODEBOX:           drawNodeboxNode(); break;
1611                 case NDT_MESH:              drawMeshNode(); break;
1612                 default:                    errorUnknownDrawtype(); break;
1613         }
1614 }
1615
1616 /*
1617         TODO: Fix alpha blending for special nodes
1618         Currently only the last element rendered is blended correct
1619 */
1620 void MapblockMeshGenerator::generate()
1621 {
1622         for (p.Z = 0; p.Z < data->side_length; p.Z++)
1623         for (p.Y = 0; p.Y < data->side_length; p.Y++)
1624         for (p.X = 0; p.X < data->side_length; p.X++) {
1625                 n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1626                 f = &nodedef->get(n);
1627                 drawNode();
1628         }
1629 }
1630
1631 void MapblockMeshGenerator::renderSingle(content_t node, u8 param2)
1632 {
1633         p = {0, 0, 0};
1634         n = MapNode(node, 0xff, param2);
1635         f = &nodedef->get(n);
1636         drawNode();
1637 }