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