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