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