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