]> git.lizzy.rs Git - dragonfireclient.git/blob - src/content_mapblock.cpp
Signlike, glasslike drawtypes: Fix inverted textures
[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, 0), v2f(1, 0), v2f(1, 1), v2f(0, 1)};
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                 // Face at Z-
635                 v3f vertices[4] = {
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                         v3f(-BS / 2, -BS / 2, -BS / 2),
640                 };
641                 for (int i = 0; i < 4; i++) {
642                         switch (face) {
643                                 case D6D_ZP: vertices[i].rotateXZBy(180); break;
644                                 case D6D_YP: vertices[i].rotateYZBy( 90); break;
645                                 case D6D_XP: vertices[i].rotateXZBy( 90); break;
646                                 case D6D_ZN: vertices[i].rotateXZBy(  0); break;
647                                 case D6D_YN: vertices[i].rotateYZBy(-90); break;
648                                 case D6D_XN: vertices[i].rotateXZBy(-90); break;
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         // Optionally render internal liquid level defined by param2
764         // Liquid is textured with 1 tile defined in nodedef 'special_tiles'
765         if (param2 > 0 && f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL &&
766                         f->special_tiles[0].texture) {
767                 // Internal liquid level has param2 range 0 .. 63,
768                 // convert it to -0.5 .. 0.5
769                 float vlev = (param2 / 63.0) * 2.0 - 1.0;
770                 tile = getSpecialTile(*f, n, 0);
771                 drawAutoLightedCuboid(aabb3f(-(nb[5] ? g : b),
772                                              -(nb[4] ? g : b),
773                                              -(nb[3] ? g : b),
774                                               (nb[2] ? g : b),
775                                               (nb[1] ? g : b) * vlev,
776                                               (nb[0] ? g : b)));
777         }
778 }
779
780 void MapblockMeshGenerator::drawAllfacesNode()
781 {
782         static const aabb3f box(-BS / 2, -BS / 2, -BS / 2, BS / 2, BS / 2, BS / 2);
783         useDefaultTile(false);
784         drawAutoLightedCuboid(box);
785 }
786
787 void MapblockMeshGenerator::drawTorchlikeNode()
788 {
789         u8 wall = n.getWallMounted(nodedef);
790         u8 tileindex = 0;
791         switch (wall) {
792                 case DWM_YP: tileindex = 1; break; // ceiling
793                 case DWM_YN: tileindex = 0; break; // floor
794                 default:     tileindex = 2; // side (or invalid—should we care?)
795         }
796         useTile(tileindex, true);
797
798         float size = BS / 2 * f->visual_scale;
799         v3f vertices[4] = {
800                 v3f(-size,  size, 0),
801                 v3f( size,  size, 0),
802                 v3f( size, -size, 0),
803                 v3f(-size, -size, 0),
804         };
805         for (int i = 0; i < 4; i++) {
806                 switch (wall) {
807                         case DWM_YP: vertices[i].rotateXZBy(-45); break;
808                         case DWM_YN: vertices[i].rotateXZBy( 45); break;
809                         case DWM_XP: vertices[i].rotateXZBy(  0); break;
810                         case DWM_XN: vertices[i].rotateXZBy(180); break;
811                         case DWM_ZP: vertices[i].rotateXZBy( 90); break;
812                         case DWM_ZN: vertices[i].rotateXZBy(-90); break;
813                 }
814         }
815         drawQuad(vertices);
816 }
817
818 void MapblockMeshGenerator::drawSignlikeNode()
819 {
820         u8 wall = n.getWallMounted(nodedef);
821         useTile(0, true);
822         static const float offset = BS / 16;
823         float size = BS / 2 * f->visual_scale;
824         // Wall at X+ of node
825         v3f vertices[4] = {
826                 v3f(BS / 2 - offset,  size,  size),
827                 v3f(BS / 2 - offset,  size, -size),
828                 v3f(BS / 2 - offset, -size, -size),
829                 v3f(BS / 2 - offset, -size,  size),
830         };
831         for (int i = 0; i < 4; i++) {
832                 switch (wall) {
833                         case DWM_YP: vertices[i].rotateXYBy( 90); break;
834                         case DWM_YN: vertices[i].rotateXYBy(-90); break;
835                         case DWM_XP: vertices[i].rotateXZBy(  0); break;
836                         case DWM_XN: vertices[i].rotateXZBy(180); break;
837                         case DWM_ZP: vertices[i].rotateXZBy( 90); break;
838                         case DWM_ZN: vertices[i].rotateXZBy(-90); break;
839                 }
840         }
841         drawQuad(vertices);
842 }
843
844 void MapblockMeshGenerator::drawPlantlikeQuad(float rotation, float quad_offset,
845         bool offset_top_only)
846 {
847         v3f vertices[4] = {
848                 v3f(-scale, -BS / 2 + scale * 2, 0),
849                 v3f( scale, -BS / 2 + scale * 2, 0),
850                 v3f( scale, -BS / 2, 0),
851                 v3f(-scale, -BS / 2, 0),
852         };
853         if (random_offset_Y) {
854                 PseudoRandom yrng(face_num++ | p.X << 16 | p.Z << 8 | p.Y << 24);
855                 offset.Y = BS * ((yrng.next() % 16 / 16.0) * 0.125);
856         }
857         int offset_count = offset_top_only ? 2 : 4;
858         for (int i = 0; i < offset_count; i++)
859                 vertices[i].Z += quad_offset;
860         for (int i = 0; i < 4; i++) {
861                 vertices[i].rotateXZBy(rotation + rotate_degree);
862                 vertices[i] += offset;
863         }
864         drawQuad(vertices);
865 }
866
867 void MapblockMeshGenerator::drawPlantlikeNode()
868 {
869         useTile(0, false);
870         draw_style = PLANT_STYLE_CROSS;
871         scale = BS / 2 * f->visual_scale;
872         offset = v3f(0, 0, 0);
873         rotate_degree = 0;
874         random_offset_Y = false;
875         face_num = 0;
876
877         switch (f->param_type_2) {
878         case CPT2_MESHOPTIONS:
879                 draw_style = PlantlikeStyle(n.param2 & MO_MASK_STYLE);
880                 if (n.param2 & MO_BIT_SCALE_SQRT2)
881                         scale *= 1.41421;
882                 if (n.param2 & MO_BIT_RANDOM_OFFSET) {
883                         PseudoRandom rng(p.X << 8 | p.Z | p.Y << 16);
884                         offset.X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
885                         offset.Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
886                 }
887                 if (n.param2 & MO_BIT_RANDOM_OFFSET_Y)
888                         random_offset_Y = true;
889                 break;
890
891         case CPT2_DEGROTATE:
892                 rotate_degree = n.param2 * 2;
893                 break;
894
895         default:
896                 break;
897         }
898
899         switch (draw_style) {
900         case PLANT_STYLE_CROSS:
901                 drawPlantlikeQuad(46);
902                 drawPlantlikeQuad(-44);
903                 break;
904
905         case PLANT_STYLE_CROSS2:
906                 drawPlantlikeQuad(91);
907                 drawPlantlikeQuad(1);
908                 break;
909
910         case PLANT_STYLE_STAR:
911                 drawPlantlikeQuad(121);
912                 drawPlantlikeQuad(241);
913                 drawPlantlikeQuad(1);
914                 break;
915
916         case PLANT_STYLE_HASH:
917                 drawPlantlikeQuad(  1, BS / 4);
918                 drawPlantlikeQuad( 91, BS / 4);
919                 drawPlantlikeQuad(181, BS / 4);
920                 drawPlantlikeQuad(271, BS / 4);
921                 break;
922
923         case PLANT_STYLE_HASH2:
924                 drawPlantlikeQuad(  1, -BS / 2, true);
925                 drawPlantlikeQuad( 91, -BS / 2, true);
926                 drawPlantlikeQuad(181, -BS / 2, true);
927                 drawPlantlikeQuad(271, -BS / 2, true);
928                 break;
929         }
930 }
931
932 void MapblockMeshGenerator::drawFirelikeQuad(float rotation, float opening_angle,
933         float offset_h, float offset_v)
934 {
935         v3f vertices[4] = {
936                 v3f(-scale, -BS / 2 + scale * 2, 0),
937                 v3f( scale, -BS / 2 + scale * 2, 0),
938                 v3f( scale, -BS / 2, 0),
939                 v3f(-scale, -BS / 2, 0),
940         };
941         for (int i = 0; i < 4; i++) {
942                 vertices[i].rotateYZBy(opening_angle);
943                 vertices[i].Z += offset_h;
944                 vertices[i].rotateXZBy(rotation);
945                 vertices[i].Y += offset_v;
946         }
947         drawQuad(vertices);
948 }
949
950 void MapblockMeshGenerator::drawFirelikeNode()
951 {
952         useTile(0, false);
953         scale = BS / 2 * f->visual_scale;
954
955         // Check for adjacent nodes
956         bool neighbors = false;
957         bool neighbor[6] = {0, 0, 0, 0, 0, 0};
958         content_t current = n.getContent();
959         for (int i = 0; i < 6; i++) {
960                 v3s16 n2p = blockpos_nodes + p + g_6dirs[i];
961                 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
962                 content_t n2c = n2.getContent();
963                 if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
964                         neighbor[i] = true;
965                         neighbors = true;
966                 }
967         }
968         bool drawBasicFire = neighbor[D6D_YN] || !neighbors;
969         bool drawBottomFire = neighbor[D6D_YP];
970
971         if (drawBasicFire || neighbor[D6D_ZP])
972                 drawFirelikeQuad(0, -10, 0.4 * BS);
973         else if (drawBottomFire)
974                 drawFirelikeQuad(0, 70, 0.47 * BS, 0.484 * BS);
975
976         if (drawBasicFire || neighbor[D6D_XN])
977                 drawFirelikeQuad(90, -10, 0.4 * BS);
978         else if (drawBottomFire)
979                 drawFirelikeQuad(90, 70, 0.47 * BS, 0.484 * BS);
980
981         if (drawBasicFire || neighbor[D6D_ZN])
982                 drawFirelikeQuad(180, -10, 0.4 * BS);
983         else if (drawBottomFire)
984                 drawFirelikeQuad(180, 70, 0.47 * BS, 0.484 * BS);
985
986         if (drawBasicFire || neighbor[D6D_XP])
987                 drawFirelikeQuad(270, -10, 0.4 * BS);
988         else if (drawBottomFire)
989                 drawFirelikeQuad(270, 70, 0.47 * BS, 0.484 * BS);
990
991         if (drawBasicFire) {
992                 drawFirelikeQuad(45, 0, 0.0);
993                 drawFirelikeQuad(-45, 0, 0.0);
994         }
995 }
996
997 void MapblockMeshGenerator::drawFencelikeNode()
998 {
999         useDefaultTile(false);
1000         TileSpec tile_nocrack = tile;
1001         tile_nocrack.material_flags &= ~MATERIAL_FLAG_CRACK;
1002
1003         // Put wood the right way around in the posts
1004         TileSpec tile_rot = tile;
1005         tile_rot.rotation = 1;
1006
1007         static const f32 post_rad = BS / 8;
1008         static const f32 bar_rad  = BS / 16;
1009         static const f32 bar_len  = BS / 2 - post_rad;
1010
1011         // The post - always present
1012         static const aabb3f post(-post_rad, -BS / 2, -post_rad,
1013                                   post_rad,  BS / 2,  post_rad);
1014         static const f32 postuv[24] = {
1015                 0.375, 0.375, 0.625, 0.625,
1016                 0.375, 0.375, 0.625, 0.625,
1017                 0.000, 0.000, 0.250, 1.000,
1018                 0.250, 0.000, 0.500, 1.000,
1019                 0.500, 0.000, 0.750, 1.000,
1020                 0.750, 0.000, 1.000, 1.000,
1021         };
1022         tile = tile_rot;
1023         drawAutoLightedCuboid(post, postuv);
1024
1025         tile = tile_nocrack;
1026
1027         // Now a section of fence, +X, if there's a post there
1028         v3s16 p2 = p;
1029         p2.X++;
1030         MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1031         const ContentFeatures *f2 = &nodedef->get(n2);
1032         if (f2->drawtype == NDT_FENCELIKE) {
1033                 static const aabb3f bar_x1(BS / 2 - bar_len,  BS / 4 - bar_rad, -bar_rad,
1034                                            BS / 2 + bar_len,  BS / 4 + bar_rad,  bar_rad);
1035                 static const aabb3f bar_x2(BS / 2 - bar_len, -BS / 4 - bar_rad, -bar_rad,
1036                                            BS / 2 + bar_len, -BS / 4 + bar_rad,  bar_rad);
1037                 static const f32 xrailuv[24] = {
1038                         0.000, 0.125, 1.000, 0.250,
1039                         0.000, 0.250, 1.000, 0.375,
1040                         0.375, 0.375, 0.500, 0.500,
1041                         0.625, 0.625, 0.750, 0.750,
1042                         0.000, 0.500, 1.000, 0.625,
1043                         0.000, 0.875, 1.000, 1.000,
1044                 };
1045                 drawAutoLightedCuboid(bar_x1, xrailuv);
1046                 drawAutoLightedCuboid(bar_x2, xrailuv);
1047         }
1048
1049         // Now a section of fence, +Z, if there's a post there
1050         p2 = p;
1051         p2.Z++;
1052         n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1053         f2 = &nodedef->get(n2);
1054         if (f2->drawtype == NDT_FENCELIKE) {
1055                 static const aabb3f bar_z1(-bar_rad,  BS / 4 - bar_rad, BS / 2 - bar_len,
1056                                             bar_rad,  BS / 4 + bar_rad, BS / 2 + bar_len);
1057                 static const aabb3f bar_z2(-bar_rad, -BS / 4 - bar_rad, BS / 2 - bar_len,
1058                                             bar_rad, -BS / 4 + bar_rad, BS / 2 + bar_len);
1059                 static const f32 zrailuv[24] = {
1060                         0.1875, 0.0625, 0.3125, 0.3125, // cannot rotate; stretch
1061                         0.2500, 0.0625, 0.3750, 0.3125, // for wood texture instead
1062                         0.0000, 0.5625, 1.0000, 0.6875,
1063                         0.0000, 0.3750, 1.0000, 0.5000,
1064                         0.3750, 0.3750, 0.5000, 0.5000,
1065                         0.6250, 0.6250, 0.7500, 0.7500,
1066                 };
1067                 drawAutoLightedCuboid(bar_z1, zrailuv);
1068                 drawAutoLightedCuboid(bar_z2, zrailuv);
1069         }
1070 }
1071
1072 bool MapblockMeshGenerator::isSameRail(v3s16 dir)
1073 {
1074         MapNode node2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
1075         if (node2.getContent() == n.getContent())
1076                 return true;
1077         const ContentFeatures &def2 = nodedef->get(node2);
1078         return ((def2.drawtype == NDT_RAILLIKE) &&
1079                 (def2.getGroup(raillike_groupname) == raillike_group));
1080 }
1081
1082 void MapblockMeshGenerator::drawRaillikeNode()
1083 {
1084         static const v3s16 direction[4] = {
1085                 v3s16( 0, 0,  1),
1086                 v3s16( 0, 0, -1),
1087                 v3s16(-1, 0,  0),
1088                 v3s16( 1, 0,  0),
1089         };
1090         static const int slope_angle[4] = {0, 180, 90, -90};
1091
1092         enum RailTile {
1093                 straight,
1094                 curved,
1095                 junction,
1096                 cross,
1097         };
1098         struct RailDesc {
1099                 int tile_index;
1100                 int angle;
1101         };
1102         static const RailDesc rail_kinds[16] = {
1103                                    // +x -x -z +z
1104                                    //-------------
1105                 {straight,   0}, //  .  .  .  .
1106                 {straight,   0}, //  .  .  . +Z
1107                 {straight,   0}, //  .  . -Z  .
1108                 {straight,   0}, //  .  . -Z +Z
1109                 {straight,  90}, //  . -X  .  .
1110                 {  curved, 180}, //  . -X  . +Z
1111                 {  curved, 270}, //  . -X -Z  .
1112                 {junction, 180}, //  . -X -Z +Z
1113                 {straight,  90}, // +X  .  .  .
1114                 {  curved,  90}, // +X  .  . +Z
1115                 {  curved,   0}, // +X  . -Z  .
1116                 {junction,   0}, // +X  . -Z +Z
1117                 {straight,  90}, // +X -X  .  .
1118                 {junction,  90}, // +X -X  . +Z
1119                 {junction, 270}, // +X -X -Z  .
1120                 {   cross,   0}, // +X -X -Z +Z
1121         };
1122
1123         raillike_group = nodedef->get(n).getGroup(raillike_groupname);
1124
1125         int code = 0;
1126         int angle;
1127         int tile_index;
1128         bool sloped = false;
1129         for (int dir = 0; dir < 4; dir++) {
1130                 bool rail_above = isSameRail(direction[dir] + v3s16(0, 1, 0));
1131                 if (rail_above) {
1132                         sloped = true;
1133                         angle = slope_angle[dir];
1134                 }
1135                 if (rail_above ||
1136                                 isSameRail(direction[dir]) ||
1137                                 isSameRail(direction[dir] + v3s16(0, -1, 0)))
1138                         code |= 1 << dir;
1139         }
1140
1141         if (sloped) {
1142                 tile_index = straight;
1143         } else {
1144                 tile_index = rail_kinds[code].tile_index;
1145                 angle = rail_kinds[code].angle;
1146         }
1147
1148         useTile(tile_index, true);
1149
1150         static const float offset = BS / 64;
1151         static const float size   = BS / 2;
1152         float y2 = sloped ? size : -size;
1153         v3f vertices[4] = {
1154                 v3f(-size,    y2 + offset,  size),
1155                 v3f( size,    y2 + offset,  size),
1156                 v3f( size, -size + offset, -size),
1157                 v3f(-size, -size + offset, -size),
1158         };
1159         if (angle)
1160                 for (int i = 0; i < 4; i++)
1161                         vertices[i].rotateXZBy(angle);
1162         drawQuad(vertices);
1163 }
1164
1165 void MapblockMeshGenerator::drawNodeboxNode()
1166 {
1167         static const v3s16 tile_dirs[6] = {
1168                 v3s16(0, 1, 0),
1169                 v3s16(0, -1, 0),
1170                 v3s16(1, 0, 0),
1171                 v3s16(-1, 0, 0),
1172                 v3s16(0, 0, 1),
1173                 v3s16(0, 0, -1)
1174         };
1175
1176         // we have this order for some reason...
1177         static const v3s16 connection_dirs[6] = {
1178                 v3s16( 0,  1,  0), // top
1179                 v3s16( 0, -1,  0), // bottom
1180                 v3s16( 0,  0, -1), // front
1181                 v3s16(-1,  0,  0), // left
1182                 v3s16( 0,  0,  1), // back
1183                 v3s16( 1,  0,  0), // right
1184         };
1185
1186         TileSpec tiles[6];
1187         for (int face = 0; face < 6; face++) {
1188                 // Handles facedir rotation for textures
1189                 tiles[face] = getTile(tile_dirs[face]);
1190         }
1191
1192         // locate possible neighboring nodes to connect to
1193         int neighbors_set = 0;
1194         if (f->node_box.type == NODEBOX_CONNECTED) {
1195                 for (int dir = 0; dir != 6; dir++) {
1196                         int flag = 1 << dir;
1197                         v3s16 p2 = blockpos_nodes + p + connection_dirs[dir];
1198                         MapNode n2 = data->m_vmanip.getNodeNoEx(p2);
1199                         if (nodedef->nodeboxConnects(n, n2, flag))
1200                                 neighbors_set |= flag;
1201                 }
1202         }
1203
1204         std::vector<aabb3f> boxes;
1205         n.getNodeBoxes(nodedef, &boxes, neighbors_set);
1206         for (std::vector<aabb3f>::iterator i = boxes.begin(); i != boxes.end(); ++i)
1207                 drawAutoLightedCuboid(*i, NULL, tiles, 6);
1208 }
1209
1210 void MapblockMeshGenerator::drawMeshNode()
1211 {
1212         u8 facedir = 0;
1213         scene::IMesh* mesh;
1214         bool private_mesh; // as a grab/drop pair is not thread-safe
1215
1216         if (f->param_type_2 == CPT2_FACEDIR ||
1217                         f->param_type_2 == CPT2_COLORED_FACEDIR) {
1218                 facedir = n.getFaceDir(nodedef);
1219         } else if (f->param_type_2 == CPT2_WALLMOUNTED ||
1220                         f->param_type_2 == CPT2_COLORED_WALLMOUNTED) {
1221                 // Convert wallmounted to 6dfacedir.
1222                 // When cache enabled, it is already converted.
1223                 facedir = n.getWallMounted(nodedef);
1224                 if (!enable_mesh_cache) {
1225                         static const u8 wm_to_6d[6] = {20, 0, 16 + 1, 12 + 3, 8, 4 + 2};
1226                         facedir = wm_to_6d[facedir];
1227                 }
1228         }
1229
1230         if (!data->m_smooth_lighting && f->mesh_ptr[facedir]) {
1231                 // use cached meshes
1232                 private_mesh = false;
1233                 mesh = f->mesh_ptr[facedir];
1234         } else if (f->mesh_ptr[0]) {
1235                 // no cache, clone and rotate mesh
1236                 private_mesh = true;
1237                 mesh = cloneMesh(f->mesh_ptr[0]);
1238                 rotateMeshBy6dFacedir(mesh, facedir);
1239                 recalculateBoundingBox(mesh);
1240                 meshmanip->recalculateNormals(mesh, true, false);
1241         } else
1242                 return;
1243
1244         int mesh_buffer_count = mesh->getMeshBufferCount();
1245         for (int j = 0; j < mesh_buffer_count; j++) {
1246                 useTile(j, false);
1247                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1248                 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1249                 int vertex_count = buf->getVertexCount();
1250
1251                 if (data->m_smooth_lighting) {
1252                         // Mesh is always private here. So the lighting is applied to each
1253                         // vertex right here.
1254                         for (int k = 0; k < vertex_count; k++) {
1255                                 video::S3DVertex &vertex = vertices[k];
1256                                 vertex.Color = blendLight(vertex.Pos, vertex.Normal, tile.color);
1257                                 vertex.Pos += origin;
1258                         }
1259                         collector->append(tile, vertices, vertex_count,
1260                                 buf->getIndices(), buf->getIndexCount());
1261                 } else {
1262                         // Don't modify the mesh, it may not be private here.
1263                         // Instead, let the collector process colors, etc.
1264                         collector->append(tile, vertices, vertex_count,
1265                                 buf->getIndices(), buf->getIndexCount(), origin,
1266                                 color, f->light_source);
1267                 }
1268         }
1269         if (private_mesh)
1270                 mesh->drop();
1271 }
1272
1273 // also called when the drawtype is known but should have been pre-converted
1274 void MapblockMeshGenerator::errorUnknownDrawtype()
1275 {
1276         infostream << "Got drawtype " << f->drawtype << std::endl;
1277         FATAL_ERROR("Unknown drawtype");
1278 }
1279
1280 void MapblockMeshGenerator::drawNode()
1281 {
1282         if (data->m_smooth_lighting)
1283                 getSmoothLightFrame();
1284         else
1285                 light = getInteriorLight(n, 1, nodedef);
1286         switch (f->drawtype) {
1287                 case NDT_LIQUID:            drawLiquidNode(false); break;
1288                 case NDT_FLOWINGLIQUID:     drawLiquidNode(true); break;
1289                 case NDT_GLASSLIKE:         drawGlasslikeNode(); break;
1290                 case NDT_GLASSLIKE_FRAMED:  drawGlasslikeFramedNode(); break;
1291                 case NDT_ALLFACES:          drawAllfacesNode(); break;
1292                 case NDT_TORCHLIKE:         drawTorchlikeNode(); break;
1293                 case NDT_SIGNLIKE:          drawSignlikeNode(); break;
1294                 case NDT_PLANTLIKE:         drawPlantlikeNode(); break;
1295                 case NDT_FIRELIKE:          drawFirelikeNode(); break;
1296                 case NDT_FENCELIKE:         drawFencelikeNode(); break;
1297                 case NDT_RAILLIKE:          drawRaillikeNode(); break;
1298                 case NDT_NODEBOX:           drawNodeboxNode(); break;
1299                 case NDT_MESH:              drawMeshNode(); break;
1300                 default:                    errorUnknownDrawtype(); break;
1301         }
1302 }
1303
1304 /*
1305         TODO: Fix alpha blending for special nodes
1306         Currently only the last element rendered is blended correct
1307 */
1308 void MapblockMeshGenerator::generate()
1309 {
1310         for (p.Z = 0; p.Z < MAP_BLOCKSIZE; p.Z++)
1311         for (p.Y = 0; p.Y < MAP_BLOCKSIZE; p.Y++)
1312         for (p.X = 0; p.X < MAP_BLOCKSIZE; p.X++) {
1313                 n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1314                 f = &nodedef->get(n);
1315                 // Solid nodes are drawn by MapBlockMesh
1316                 if (f->solidness != 0)
1317                         continue;
1318                 if (f->drawtype == NDT_AIRLIKE)
1319                         continue;
1320                 origin = intToFloat(p, BS);
1321                 drawNode();
1322         }
1323 }