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