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