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