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