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