]> git.lizzy.rs Git - minetest.git/blob - src/client/content_mapblock.cpp
90284ecce51c9f03c6ea53d7883a823fbd402a67
[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;
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                 rotate_degree = n.param2 * 2;
992                 break;
993
994         case CPT2_LEVELED:
995                 plant_height = n.param2 / 16.0;
996                 break;
997
998         default:
999                 break;
1000         }
1001
1002         switch (draw_style) {
1003         case PLANT_STYLE_CROSS:
1004                 drawPlantlikeQuad(46);
1005                 drawPlantlikeQuad(-44);
1006                 break;
1007
1008         case PLANT_STYLE_CROSS2:
1009                 drawPlantlikeQuad(91);
1010                 drawPlantlikeQuad(1);
1011                 break;
1012
1013         case PLANT_STYLE_STAR:
1014                 drawPlantlikeQuad(121);
1015                 drawPlantlikeQuad(241);
1016                 drawPlantlikeQuad(1);
1017                 break;
1018
1019         case PLANT_STYLE_HASH:
1020                 drawPlantlikeQuad(  1, BS / 4);
1021                 drawPlantlikeQuad( 91, BS / 4);
1022                 drawPlantlikeQuad(181, BS / 4);
1023                 drawPlantlikeQuad(271, BS / 4);
1024                 break;
1025
1026         case PLANT_STYLE_HASH2:
1027                 drawPlantlikeQuad(  1, -BS / 2, true);
1028                 drawPlantlikeQuad( 91, -BS / 2, true);
1029                 drawPlantlikeQuad(181, -BS / 2, true);
1030                 drawPlantlikeQuad(271, -BS / 2, true);
1031                 break;
1032         }
1033 }
1034
1035 void MapblockMeshGenerator::drawPlantlikeNode()
1036 {
1037         useTile();
1038         drawPlantlike();
1039 }
1040
1041 void MapblockMeshGenerator::drawPlantlikeRootedNode()
1042 {
1043         useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, 0, true);
1044         origin += v3f(0.0, BS, 0.0);
1045         p.Y++;
1046         if (data->m_smooth_lighting) {
1047                 getSmoothLightFrame();
1048         } else {
1049                 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1050                 light = LightPair(getInteriorLight(ntop, 1, nodedef));
1051         }
1052         drawPlantlike();
1053         p.Y--;
1054 }
1055
1056 void MapblockMeshGenerator::drawFirelikeQuad(float rotation, float opening_angle,
1057         float offset_h, float offset_v)
1058 {
1059         v3f vertices[4] = {
1060                 v3f(-scale, -BS / 2 + scale * 2, 0),
1061                 v3f( scale, -BS / 2 + scale * 2, 0),
1062                 v3f( scale, -BS / 2, 0),
1063                 v3f(-scale, -BS / 2, 0),
1064         };
1065
1066         for (v3f &vertex : vertices) {
1067                 vertex.rotateYZBy(opening_angle);
1068                 vertex.Z += offset_h;
1069                 vertex.rotateXZBy(rotation);
1070                 vertex.Y += offset_v;
1071         }
1072         drawQuad(vertices);
1073 }
1074
1075 void MapblockMeshGenerator::drawFirelikeNode()
1076 {
1077         useTile();
1078         scale = BS / 2 * f->visual_scale;
1079
1080         // Check for adjacent nodes
1081         bool neighbors = false;
1082         bool neighbor[6] = {0, 0, 0, 0, 0, 0};
1083         content_t current = n.getContent();
1084         for (int i = 0; i < 6; i++) {
1085                 v3s16 n2p = blockpos_nodes + p + g_6dirs[i];
1086                 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
1087                 content_t n2c = n2.getContent();
1088                 if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
1089                         neighbor[i] = true;
1090                         neighbors = true;
1091                 }
1092         }
1093         bool drawBasicFire = neighbor[D6D_YN] || !neighbors;
1094         bool drawBottomFire = neighbor[D6D_YP];
1095
1096         if (drawBasicFire || neighbor[D6D_ZP])
1097                 drawFirelikeQuad(0, -10, 0.4 * BS);
1098         else if (drawBottomFire)
1099                 drawFirelikeQuad(0, 70, 0.47 * BS, 0.484 * BS);
1100
1101         if (drawBasicFire || neighbor[D6D_XN])
1102                 drawFirelikeQuad(90, -10, 0.4 * BS);
1103         else if (drawBottomFire)
1104                 drawFirelikeQuad(90, 70, 0.47 * BS, 0.484 * BS);
1105
1106         if (drawBasicFire || neighbor[D6D_ZN])
1107                 drawFirelikeQuad(180, -10, 0.4 * BS);
1108         else if (drawBottomFire)
1109                 drawFirelikeQuad(180, 70, 0.47 * BS, 0.484 * BS);
1110
1111         if (drawBasicFire || neighbor[D6D_XP])
1112                 drawFirelikeQuad(270, -10, 0.4 * BS);
1113         else if (drawBottomFire)
1114                 drawFirelikeQuad(270, 70, 0.47 * BS, 0.484 * BS);
1115
1116         if (drawBasicFire) {
1117                 drawFirelikeQuad(45, 0, 0.0);
1118                 drawFirelikeQuad(-45, 0, 0.0);
1119         }
1120 }
1121
1122 void MapblockMeshGenerator::drawFencelikeNode()
1123 {
1124         useTile(0, 0, 0);
1125         TileSpec tile_nocrack = tile;
1126
1127         for (auto &layer : tile_nocrack.layers)
1128                 layer.material_flags &= ~MATERIAL_FLAG_CRACK;
1129
1130         // Put wood the right way around in the posts
1131         TileSpec tile_rot = tile;
1132         tile_rot.rotation = 1;
1133
1134         static const f32 post_rad = BS / 8;
1135         static const f32 bar_rad  = BS / 16;
1136         static const f32 bar_len  = BS / 2 - post_rad;
1137
1138         // The post - always present
1139         static const aabb3f post(-post_rad, -BS / 2, -post_rad,
1140                                   post_rad,  BS / 2,  post_rad);
1141         static const f32 postuv[24] = {
1142                 0.375, 0.375, 0.625, 0.625,
1143                 0.375, 0.375, 0.625, 0.625,
1144                 0.000, 0.000, 0.250, 1.000,
1145                 0.250, 0.000, 0.500, 1.000,
1146                 0.500, 0.000, 0.750, 1.000,
1147                 0.750, 0.000, 1.000, 1.000,
1148         };
1149         tile = tile_rot;
1150         drawAutoLightedCuboid(post, postuv);
1151
1152         tile = tile_nocrack;
1153
1154         // Now a section of fence, +X, if there's a post there
1155         v3s16 p2 = p;
1156         p2.X++;
1157         MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1158         const ContentFeatures *f2 = &nodedef->get(n2);
1159         if (f2->drawtype == NDT_FENCELIKE) {
1160                 static const aabb3f bar_x1(BS / 2 - bar_len,  BS / 4 - bar_rad, -bar_rad,
1161                                            BS / 2 + bar_len,  BS / 4 + bar_rad,  bar_rad);
1162                 static const aabb3f bar_x2(BS / 2 - bar_len, -BS / 4 - bar_rad, -bar_rad,
1163                                            BS / 2 + bar_len, -BS / 4 + bar_rad,  bar_rad);
1164                 static const f32 xrailuv[24] = {
1165                         0.000, 0.125, 1.000, 0.250,
1166                         0.000, 0.250, 1.000, 0.375,
1167                         0.375, 0.375, 0.500, 0.500,
1168                         0.625, 0.625, 0.750, 0.750,
1169                         0.000, 0.500, 1.000, 0.625,
1170                         0.000, 0.875, 1.000, 1.000,
1171                 };
1172                 drawAutoLightedCuboid(bar_x1, xrailuv);
1173                 drawAutoLightedCuboid(bar_x2, xrailuv);
1174         }
1175
1176         // Now a section of fence, +Z, if there's a post there
1177         p2 = p;
1178         p2.Z++;
1179         n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1180         f2 = &nodedef->get(n2);
1181         if (f2->drawtype == NDT_FENCELIKE) {
1182                 static const aabb3f bar_z1(-bar_rad,  BS / 4 - bar_rad, BS / 2 - bar_len,
1183                                             bar_rad,  BS / 4 + bar_rad, BS / 2 + bar_len);
1184                 static const aabb3f bar_z2(-bar_rad, -BS / 4 - bar_rad, BS / 2 - bar_len,
1185                                             bar_rad, -BS / 4 + bar_rad, BS / 2 + bar_len);
1186                 static const f32 zrailuv[24] = {
1187                         0.1875, 0.0625, 0.3125, 0.3125, // cannot rotate; stretch
1188                         0.2500, 0.0625, 0.3750, 0.3125, // for wood texture instead
1189                         0.0000, 0.5625, 1.0000, 0.6875,
1190                         0.0000, 0.3750, 1.0000, 0.5000,
1191                         0.3750, 0.3750, 0.5000, 0.5000,
1192                         0.6250, 0.6250, 0.7500, 0.7500,
1193                 };
1194                 drawAutoLightedCuboid(bar_z1, zrailuv);
1195                 drawAutoLightedCuboid(bar_z2, zrailuv);
1196         }
1197 }
1198
1199 bool MapblockMeshGenerator::isSameRail(v3s16 dir)
1200 {
1201         MapNode node2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
1202         if (node2.getContent() == n.getContent())
1203                 return true;
1204         const ContentFeatures &def2 = nodedef->get(node2);
1205         return ((def2.drawtype == NDT_RAILLIKE) &&
1206                 (def2.getGroup(raillike_groupname) == raillike_group));
1207 }
1208
1209 namespace {
1210         static const v3s16 rail_direction[4] = {
1211                 v3s16( 0, 0,  1),
1212                 v3s16( 0, 0, -1),
1213                 v3s16(-1, 0,  0),
1214                 v3s16( 1, 0,  0),
1215         };
1216         static const int rail_slope_angle[4] = {0, 180, 90, -90};
1217
1218         enum RailTile {
1219                 straight,
1220                 curved,
1221                 junction,
1222                 cross,
1223         };
1224         struct RailDesc {
1225                 int tile_index;
1226                 int angle;
1227         };
1228         static const RailDesc rail_kinds[16] = {
1229                                  // +x -x -z +z
1230                                  //-------------
1231                 {straight,   0}, //  .  .  .  .
1232                 {straight,   0}, //  .  .  . +Z
1233                 {straight,   0}, //  .  . -Z  .
1234                 {straight,   0}, //  .  . -Z +Z
1235                 {straight,  90}, //  . -X  .  .
1236                 {  curved, 180}, //  . -X  . +Z
1237                 {  curved, 270}, //  . -X -Z  .
1238                 {junction, 180}, //  . -X -Z +Z
1239                 {straight,  90}, // +X  .  .  .
1240                 {  curved,  90}, // +X  .  . +Z
1241                 {  curved,   0}, // +X  . -Z  .
1242                 {junction,   0}, // +X  . -Z +Z
1243                 {straight,  90}, // +X -X  .  .
1244                 {junction,  90}, // +X -X  . +Z
1245                 {junction, 270}, // +X -X -Z  .
1246                 {   cross,   0}, // +X -X -Z +Z
1247         };
1248 }
1249
1250 void MapblockMeshGenerator::drawRaillikeNode()
1251 {
1252         raillike_group = nodedef->get(n).getGroup(raillike_groupname);
1253
1254         int code = 0;
1255         int angle;
1256         int tile_index;
1257         bool sloped = false;
1258         for (int dir = 0; dir < 4; dir++) {
1259                 bool rail_above = isSameRail(rail_direction[dir] + v3s16(0, 1, 0));
1260                 if (rail_above) {
1261                         sloped = true;
1262                         angle = rail_slope_angle[dir];
1263                 }
1264                 if (rail_above ||
1265                                 isSameRail(rail_direction[dir]) ||
1266                                 isSameRail(rail_direction[dir] + v3s16(0, -1, 0)))
1267                         code |= 1 << dir;
1268         }
1269
1270         if (sloped) {
1271                 tile_index = straight;
1272         } else {
1273                 tile_index = rail_kinds[code].tile_index;
1274                 angle = rail_kinds[code].angle;
1275         }
1276
1277         useTile(tile_index, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
1278
1279         static const float offset = BS / 64;
1280         static const float size   = BS / 2;
1281         float y2 = sloped ? size : -size;
1282         v3f vertices[4] = {
1283                 v3f(-size,    y2 + offset,  size),
1284                 v3f( size,    y2 + offset,  size),
1285                 v3f( size, -size + offset, -size),
1286                 v3f(-size, -size + offset, -size),
1287         };
1288         if (angle)
1289                 for (v3f &vertex : vertices)
1290                         vertex.rotateXZBy(angle);
1291         drawQuad(vertices);
1292 }
1293
1294 namespace {
1295         static const v3s16 nodebox_tile_dirs[6] = {
1296                 v3s16(0, 1, 0),
1297                 v3s16(0, -1, 0),
1298                 v3s16(1, 0, 0),
1299                 v3s16(-1, 0, 0),
1300                 v3s16(0, 0, 1),
1301                 v3s16(0, 0, -1)
1302         };
1303
1304         // we have this order for some reason...
1305         static const v3s16 nodebox_connection_dirs[6] = {
1306                 v3s16( 0,  1,  0), // top
1307                 v3s16( 0, -1,  0), // bottom
1308                 v3s16( 0,  0, -1), // front
1309                 v3s16(-1,  0,  0), // left
1310                 v3s16( 0,  0,  1), // back
1311                 v3s16( 1,  0,  0), // right
1312         };
1313 }
1314
1315 void MapblockMeshGenerator::drawNodeboxNode()
1316 {
1317         TileSpec tiles[6];
1318         for (int face = 0; face < 6; face++) {
1319                 // Handles facedir rotation for textures
1320                 getTile(nodebox_tile_dirs[face], &tiles[face]);
1321         }
1322
1323         // locate possible neighboring nodes to connect to
1324         u8 neighbors_set = 0;
1325         if (f->node_box.type == NODEBOX_CONNECTED) {
1326                 for (int dir = 0; dir != 6; dir++) {
1327                         u8 flag = 1 << dir;
1328                         v3s16 p2 = blockpos_nodes + p + nodebox_connection_dirs[dir];
1329                         MapNode n2 = data->m_vmanip.getNodeNoEx(p2);
1330                         if (nodedef->nodeboxConnects(n, n2, flag))
1331                                 neighbors_set |= flag;
1332                 }
1333         }
1334
1335         std::vector<aabb3f> boxes;
1336         n.getNodeBoxes(nodedef, &boxes, neighbors_set);
1337         for (auto &box : boxes)
1338                 drawAutoLightedCuboid(box, nullptr, tiles, 6);
1339 }
1340
1341 void MapblockMeshGenerator::drawMeshNode()
1342 {
1343         u8 facedir = 0;
1344         scene::IMesh* mesh;
1345         bool private_mesh; // as a grab/drop pair is not thread-safe
1346
1347         if (f->param_type_2 == CPT2_FACEDIR ||
1348                         f->param_type_2 == CPT2_COLORED_FACEDIR) {
1349                 facedir = n.getFaceDir(nodedef);
1350         } else if (f->param_type_2 == CPT2_WALLMOUNTED ||
1351                         f->param_type_2 == CPT2_COLORED_WALLMOUNTED) {
1352                 // Convert wallmounted to 6dfacedir.
1353                 // When cache enabled, it is already converted.
1354                 facedir = n.getWallMounted(nodedef);
1355                 if (!enable_mesh_cache)
1356                         facedir = wallmounted_to_facedir[facedir];
1357         }
1358
1359         if (!data->m_smooth_lighting && f->mesh_ptr[facedir]) {
1360                 // use cached meshes
1361                 private_mesh = false;
1362                 mesh = f->mesh_ptr[facedir];
1363         } else if (f->mesh_ptr[0]) {
1364                 // no cache, clone and rotate mesh
1365                 private_mesh = true;
1366                 mesh = cloneMesh(f->mesh_ptr[0]);
1367                 rotateMeshBy6dFacedir(mesh, facedir);
1368                 recalculateBoundingBox(mesh);
1369                 meshmanip->recalculateNormals(mesh, true, false);
1370         } else
1371                 return;
1372
1373         int mesh_buffer_count = mesh->getMeshBufferCount();
1374         for (int j = 0; j < mesh_buffer_count; j++) {
1375                 useTile(j);
1376                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1377                 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1378                 int vertex_count = buf->getVertexCount();
1379
1380                 if (data->m_smooth_lighting) {
1381                         // Mesh is always private here. So the lighting is applied to each
1382                         // vertex right here.
1383                         for (int k = 0; k < vertex_count; k++) {
1384                                 video::S3DVertex &vertex = vertices[k];
1385                                 vertex.Color = blendLightColor(vertex.Pos, vertex.Normal);
1386                                 vertex.Pos += origin;
1387                         }
1388                         collector->append(tile, vertices, vertex_count,
1389                                 buf->getIndices(), buf->getIndexCount());
1390                 } else {
1391                         // Don't modify the mesh, it may not be private here.
1392                         // Instead, let the collector process colors, etc.
1393                         collector->append(tile, vertices, vertex_count,
1394                                 buf->getIndices(), buf->getIndexCount(), origin,
1395                                 color, f->light_source);
1396                 }
1397         }
1398         if (private_mesh)
1399                 mesh->drop();
1400 }
1401
1402 // also called when the drawtype is known but should have been pre-converted
1403 void MapblockMeshGenerator::errorUnknownDrawtype()
1404 {
1405         infostream << "Got drawtype " << f->drawtype << std::endl;
1406         FATAL_ERROR("Unknown drawtype");
1407 }
1408
1409 void MapblockMeshGenerator::drawNode()
1410 {
1411         // skip some drawtypes early
1412         switch (f->drawtype) {
1413                 case NDT_NORMAL:   // Drawn by MapBlockMesh
1414                 case NDT_AIRLIKE:  // Not drawn at all
1415                 case NDT_LIQUID:   // Drawn by MapBlockMesh
1416                         return;
1417                 default:
1418                         break;
1419         }
1420         origin = intToFloat(p, BS);
1421         if (data->m_smooth_lighting)
1422                 getSmoothLightFrame();
1423         else
1424                 light = LightPair(getInteriorLight(n, 1, nodedef));
1425         switch (f->drawtype) {
1426                 case NDT_FLOWINGLIQUID:     drawLiquidNode(); break;
1427                 case NDT_GLASSLIKE:         drawGlasslikeNode(); break;
1428                 case NDT_GLASSLIKE_FRAMED:  drawGlasslikeFramedNode(); break;
1429                 case NDT_ALLFACES:          drawAllfacesNode(); break;
1430                 case NDT_TORCHLIKE:         drawTorchlikeNode(); break;
1431                 case NDT_SIGNLIKE:          drawSignlikeNode(); break;
1432                 case NDT_PLANTLIKE:         drawPlantlikeNode(); break;
1433                 case NDT_PLANTLIKE_ROOTED:  drawPlantlikeRootedNode(); break;
1434                 case NDT_FIRELIKE:          drawFirelikeNode(); break;
1435                 case NDT_FENCELIKE:         drawFencelikeNode(); break;
1436                 case NDT_RAILLIKE:          drawRaillikeNode(); break;
1437                 case NDT_NODEBOX:           drawNodeboxNode(); break;
1438                 case NDT_MESH:              drawMeshNode(); break;
1439                 default:                    errorUnknownDrawtype(); break;
1440         }
1441 }
1442
1443 /*
1444         TODO: Fix alpha blending for special nodes
1445         Currently only the last element rendered is blended correct
1446 */
1447 void MapblockMeshGenerator::generate()
1448 {
1449         for (p.Z = 0; p.Z < MAP_BLOCKSIZE; p.Z++)
1450         for (p.Y = 0; p.Y < MAP_BLOCKSIZE; p.Y++)
1451         for (p.X = 0; p.X < MAP_BLOCKSIZE; p.X++) {
1452                 n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1453                 f = &nodedef->get(n);
1454                 drawNode();
1455         }
1456 }
1457
1458 void MapblockMeshGenerator::renderSingle(content_t node, u8 param2)
1459 {
1460         p = {0, 0, 0};
1461         n = MapNode(node, 0xff, param2);
1462         f = &nodedef->get(n);
1463         drawNode();
1464 }