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