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