]> git.lizzy.rs Git - dragonfireclient.git/blob - src/content_mapblock.cpp
Update Android defaults for modern H/W. (#7572)
[dragonfireclient.git] / src / content_mapblock.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "content_mapblock.h"
21 #include "util/numeric.h"
22 #include "util/directiontables.h"
23 #include "mapblock_mesh.h"
24 #include "settings.h"
25 #include "nodedef.h"
26 #include "client/tile.h"
27 #include "mesh.h"
28 #include <IMeshManipulator.h>
29 #include "client/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         f32 texture_coord_buf[24];
370         f32 dx1 = box.MinEdge.X;
371         f32 dy1 = box.MinEdge.Y;
372         f32 dz1 = box.MinEdge.Z;
373         f32 dx2 = box.MaxEdge.X;
374         f32 dy2 = box.MaxEdge.Y;
375         f32 dz2 = box.MaxEdge.Z;
376         box.MinEdge += origin;
377         box.MaxEdge += origin;
378         if (!txc) {
379                 generateCuboidTextureCoords(box, texture_coord_buf);
380                 txc = texture_coord_buf;
381         }
382         if (!tiles) {
383                 tiles = &tile;
384                 tile_count = 1;
385         }
386         if (data->m_smooth_lighting) {
387                 LightInfo lights[8];
388                 for (int j = 0; j < 8; ++j) {
389                         v3f d;
390                         d.X = (j & 4) ? dx2 : dx1;
391                         d.Y = (j & 2) ? dy2 : dy1;
392                         d.Z = (j & 1) ? dz2 : dz1;
393                         lights[j] = blendLight(d);
394                 }
395                 drawCuboid(box, tiles, tile_count, lights, txc);
396         } else {
397                 drawCuboid(box, tiles, tile_count, nullptr, txc);
398         }
399 }
400
401 void MapblockMeshGenerator::prepareLiquidNodeDrawing()
402 {
403         getSpecialTile(0, &tile_liquid_top);
404         getSpecialTile(1, &tile_liquid);
405
406         MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y + 1, p.Z));
407         MapNode nbottom = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y - 1, p.Z));
408         c_flowing = nodedef->getId(f->liquid_alternative_flowing);
409         c_source = nodedef->getId(f->liquid_alternative_source);
410         top_is_same_liquid = (ntop.getContent() == c_flowing) || (ntop.getContent() == c_source);
411         draw_liquid_bottom = (nbottom.getContent() != c_flowing) && (nbottom.getContent() != c_source);
412         if (draw_liquid_bottom) {
413                 const ContentFeatures &f2 = nodedef->get(nbottom.getContent());
414                 if (f2.solidness > 1)
415                         draw_liquid_bottom = false;
416         }
417
418         if (data->m_smooth_lighting)
419                 return; // don't need to pre-compute anything in this case
420
421         if (f->light_source != 0) {
422                 // If this liquid emits light and doesn't contain light, draw
423                 // it at what it emits, for an increased effect
424                 u8 e = decode_light(f->light_source);
425                 light = LightPair(std::max(e, light.lightDay), std::max(e, light.lightNight));
426         } else if (nodedef->get(ntop).param_type == CPT_LIGHT) {
427                 // Otherwise, use the light of the node on top if possible
428                 light = LightPair(getInteriorLight(ntop, 0, nodedef));
429         }
430
431         color_liquid_top = encode_light(light, f->light_source);
432         color = encode_light(light, f->light_source);
433 }
434
435 void MapblockMeshGenerator::getLiquidNeighborhood()
436 {
437         u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8);
438
439         for (int w = -1; w <= 1; w++)
440         for (int u = -1; u <= 1; u++) {
441                 NeighborData &neighbor = liquid_neighbors[w + 1][u + 1];
442                 v3s16 p2 = p + v3s16(u, 0, w);
443                 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
444                 neighbor.content = n2.getContent();
445                 neighbor.level = -0.5 * BS;
446                 neighbor.is_same_liquid = false;
447                 neighbor.top_is_same_liquid = false;
448
449                 if (neighbor.content == CONTENT_IGNORE)
450                         continue;
451
452                 if (neighbor.content == c_source) {
453                         neighbor.is_same_liquid = true;
454                         neighbor.level = 0.5 * BS;
455                 } else if (neighbor.content == c_flowing) {
456                         neighbor.is_same_liquid = true;
457                         u8 liquid_level = (n2.param2 & LIQUID_LEVEL_MASK);
458                         if (liquid_level <= LIQUID_LEVEL_MAX + 1 - range)
459                                 liquid_level = 0;
460                         else
461                                 liquid_level -= (LIQUID_LEVEL_MAX + 1 - range);
462                         neighbor.level = (-0.5 + (liquid_level + 0.5) / range) * BS;
463                 }
464
465                 // Check node above neighbor.
466                 // NOTE: This doesn't get executed if neighbor
467                 //       doesn't exist
468                 p2.Y++;
469                 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
470                 if (n2.getContent() == c_source || n2.getContent() == c_flowing)
471                         neighbor.top_is_same_liquid = true;
472         }
473 }
474
475 void MapblockMeshGenerator::calculateCornerLevels()
476 {
477         for (int k = 0; k < 2; k++)
478         for (int i = 0; i < 2; i++)
479                 corner_levels[k][i] = getCornerLevel(i, k);
480 }
481
482 f32 MapblockMeshGenerator::getCornerLevel(int i, int k)
483 {
484         float sum = 0;
485         int count = 0;
486         int air_count = 0;
487         for (int dk = 0; dk < 2; dk++)
488         for (int di = 0; di < 2; di++) {
489                 NeighborData &neighbor_data = liquid_neighbors[k + dk][i + di];
490                 content_t content = neighbor_data.content;
491
492                 // If top is liquid, draw starting from top of node
493                 if (neighbor_data.top_is_same_liquid)
494                         return 0.5 * BS;
495
496                 // Source always has the full height
497                 if (content == c_source)
498                         return 0.5 * BS;
499
500                 // Flowing liquid has level information
501                 if (content == c_flowing) {
502                         sum += neighbor_data.level;
503                         count++;
504                 } else if (content == CONTENT_AIR) {
505                         air_count++;
506                         if (air_count >= 2)
507                                 return -0.5 * BS + 0.2;
508                 }
509         }
510         if (count > 0)
511                 return sum / count;
512         return 0;
513 }
514
515 void MapblockMeshGenerator::drawLiquidSides()
516 {
517         struct LiquidFaceDesc {
518                 v3s16 dir; // XZ
519                 v3s16 p[2]; // XZ only; 1 means +, 0 means -
520         };
521         struct UV {
522                 int u, v;
523         };
524         static const LiquidFaceDesc base_faces[4] = {
525                 {v3s16( 1, 0,  0), {v3s16(1, 0, 1), v3s16(1, 0, 0)}},
526                 {v3s16(-1, 0,  0), {v3s16(0, 0, 0), v3s16(0, 0, 1)}},
527                 {v3s16( 0, 0,  1), {v3s16(0, 0, 1), v3s16(1, 0, 1)}},
528                 {v3s16( 0, 0, -1), {v3s16(1, 0, 0), v3s16(0, 0, 0)}},
529         };
530         static const UV base_vertices[4] = {
531                 {0, 1},
532                 {1, 1},
533                 {1, 0},
534                 {0, 0}
535         };
536
537         for (const auto &face : base_faces) {
538                 const NeighborData &neighbor = liquid_neighbors[face.dir.Z + 1][face.dir.X + 1];
539
540                 // No face between nodes of the same liquid, unless there is node
541                 // at the top to which it should be connected. Again, unless the face
542                 // there would be inside the liquid
543                 if (neighbor.is_same_liquid) {
544                         if (!top_is_same_liquid)
545                                 continue;
546                         if (neighbor.top_is_same_liquid)
547                                 continue;
548                 }
549
550                 const ContentFeatures &neighbor_features = nodedef->get(neighbor.content);
551                 // Don't draw face if neighbor is blocking the view
552                 if (neighbor_features.solidness == 2)
553                         continue;
554
555                 video::S3DVertex vertices[4];
556                 for (int j = 0; j < 4; j++) {
557                         const UV &vertex = base_vertices[j];
558                         const v3s16 &base = face.p[vertex.u];
559                         v3f pos;
560                         pos.X = (base.X - 0.5) * BS;
561                         pos.Z = (base.Z - 0.5) * BS;
562                         if (vertex.v)
563                                 pos.Y = neighbor.is_same_liquid ? corner_levels[base.Z][base.X] : -0.5 * BS;
564                         else
565                                 pos.Y =     !top_is_same_liquid ? corner_levels[base.Z][base.X] :  0.5 * BS;
566                         if (data->m_smooth_lighting)
567                                 color = blendLightColor(pos);
568                         pos += origin;
569                         vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, color, vertex.u, vertex.v);
570                 };
571                 collector->append(tile_liquid, vertices, 4, quad_indices, 6);
572         }
573 }
574
575 void MapblockMeshGenerator::drawLiquidTop()
576 {
577         // To get backface culling right, the vertices need to go
578         // clockwise around the front of the face. And we happened to
579         // calculate corner levels in exact reverse order.
580         static const int corner_resolve[4][2] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
581
582         video::S3DVertex vertices[4] = {
583                 video::S3DVertex(-BS / 2, 0,  BS / 2, 0, 0, 0, color_liquid_top, 0, 1),
584                 video::S3DVertex( BS / 2, 0,  BS / 2, 0, 0, 0, color_liquid_top, 1, 1),
585                 video::S3DVertex( BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 1, 0),
586                 video::S3DVertex(-BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 0, 0),
587         };
588
589         for (int i = 0; i < 4; i++) {
590                 int u = corner_resolve[i][0];
591                 int w = corner_resolve[i][1];
592                 vertices[i].Pos.Y += corner_levels[w][u];
593                 if (data->m_smooth_lighting)
594                         vertices[i].Color = blendLightColor(vertices[i].Pos);
595                 vertices[i].Pos += origin;
596         }
597
598         // Default downwards-flowing texture animation goes from
599         // -Z towards +Z, thus the direction is +Z.
600         // Rotate texture to make animation go in flow direction
601         // Positive if liquid moves towards +Z
602         f32 dz = (corner_levels[0][0] + corner_levels[0][1]) -
603                  (corner_levels[1][0] + corner_levels[1][1]);
604         // Positive if liquid moves towards +X
605         f32 dx = (corner_levels[0][0] + corner_levels[1][0]) -
606                  (corner_levels[0][1] + corner_levels[1][1]);
607         f32 tcoord_angle = atan2(dz, dx) * core::RADTODEG;
608         v2f tcoord_center(0.5, 0.5);
609         v2f tcoord_translate(blockpos_nodes.Z + p.Z, blockpos_nodes.X + p.X);
610         tcoord_translate.rotateBy(tcoord_angle);
611         tcoord_translate.X -= floor(tcoord_translate.X);
612         tcoord_translate.Y -= floor(tcoord_translate.Y);
613
614         for (video::S3DVertex &vertex : vertices) {
615                 vertex.TCoords.rotateBy(tcoord_angle, tcoord_center);
616                 vertex.TCoords += tcoord_translate;
617         }
618
619         std::swap(vertices[0].TCoords, vertices[2].TCoords);
620
621         collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
622 }
623
624 void MapblockMeshGenerator::drawLiquidBottom()
625 {
626         video::S3DVertex vertices[4] = {
627                 video::S3DVertex(-BS / 2, -BS / 2, -BS / 2, 0, 0, 0, color_liquid_top, 0, 0),
628                 video::S3DVertex( BS / 2, -BS / 2, -BS / 2, 0, 0, 0, color_liquid_top, 1, 0),
629                 video::S3DVertex( BS / 2, -BS / 2,  BS / 2, 0, 0, 0, color_liquid_top, 1, 1),
630                 video::S3DVertex(-BS / 2, -BS / 2,  BS / 2, 0, 0, 0, color_liquid_top, 0, 1),
631         };
632
633         for (int i = 0; i < 4; i++) {
634                 if (data->m_smooth_lighting)
635                         vertices[i].Color = blendLightColor(vertices[i].Pos);
636                 vertices[i].Pos += origin;
637         }
638
639         collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
640 }
641
642 void MapblockMeshGenerator::drawLiquidNode()
643 {
644         prepareLiquidNodeDrawing();
645         getLiquidNeighborhood();
646         calculateCornerLevels();
647         drawLiquidSides();
648         if (!top_is_same_liquid)
649                 drawLiquidTop();
650         if (draw_liquid_bottom)
651                 drawLiquidBottom();
652 }
653
654 void MapblockMeshGenerator::drawGlasslikeNode()
655 {
656         useTile(0, 0, 0);
657
658         for (int face = 0; face < 6; face++) {
659                 // Check this neighbor
660                 v3s16 dir = g_6dirs[face];
661                 v3s16 neighbor_pos = blockpos_nodes + p + dir;
662                 MapNode neighbor = data->m_vmanip.getNodeNoExNoEmerge(neighbor_pos);
663                 // Don't make face if neighbor is of same type
664                 if (neighbor.getContent() == n.getContent())
665                         continue;
666                 // Face at Z-
667                 v3f vertices[4] = {
668                         v3f(-BS / 2,  BS / 2, -BS / 2),
669                         v3f( BS / 2,  BS / 2, -BS / 2),
670                         v3f( BS / 2, -BS / 2, -BS / 2),
671                         v3f(-BS / 2, -BS / 2, -BS / 2),
672                 };
673
674                 for (v3f &vertex : vertices) {
675                         switch (face) {
676                                 case D6D_ZP:
677                                         vertex.rotateXZBy(180); break;
678                                 case D6D_YP:
679                                         vertex.rotateYZBy( 90); break;
680                                 case D6D_XP:
681                                         vertex.rotateXZBy( 90); break;
682                                 case D6D_ZN:
683                                         vertex.rotateXZBy(  0); break;
684                                 case D6D_YN:
685                                         vertex.rotateYZBy(-90); break;
686                                 case D6D_XN:
687                                         vertex.rotateXZBy(-90); break;
688                         }
689                 }
690                 drawQuad(vertices, dir);
691         }
692 }
693
694 void MapblockMeshGenerator::drawGlasslikeFramedNode()
695 {
696         TileSpec tiles[6];
697         for (int face = 0; face < 6; face++)
698                 getTile(g_6dirs[face], &tiles[face]);
699
700         TileSpec glass_tiles[6];
701         if (tiles[1].layers[0].texture &&
702                         tiles[2].layers[0].texture &&
703                         tiles[3].layers[0].texture) {
704                 glass_tiles[0] = tiles[4];
705                 glass_tiles[1] = tiles[0];
706                 glass_tiles[2] = tiles[4];
707                 glass_tiles[3] = tiles[4];
708                 glass_tiles[4] = tiles[3];
709                 glass_tiles[5] = tiles[4];
710         } else {
711                 for (auto &glass_tile : glass_tiles)
712                         glass_tile = tiles[4];
713         }
714
715         u8 param2 = n.getParam2();
716         bool H_merge = !(param2 & 128);
717         bool V_merge = !(param2 & 64);
718         param2 &= 63;
719
720         static const float a = BS / 2;
721         static const float g = a - 0.003;
722         static const float b = .876 * ( BS / 2 );
723
724         static const aabb3f frame_edges[FRAMED_EDGE_COUNT] = {
725                 aabb3f( b,  b, -a,  a,  a,  a), // y+
726                 aabb3f(-a,  b, -a, -b,  a,  a), // y+
727                 aabb3f( b, -a, -a,  a, -b,  a), // y-
728                 aabb3f(-a, -a, -a, -b, -b,  a), // y-
729                 aabb3f( b, -a,  b,  a,  a,  a), // x+
730                 aabb3f( b, -a, -a,  a,  a, -b), // x+
731                 aabb3f(-a, -a,  b, -b,  a,  a), // x-
732                 aabb3f(-a, -a, -a, -b,  a, -b), // x-
733                 aabb3f(-a,  b,  b,  a,  a,  a), // z+
734                 aabb3f(-a, -a,  b,  a, -b,  a), // z+
735                 aabb3f(-a, -a, -a,  a, -b, -b), // z-
736                 aabb3f(-a,  b, -a,  a,  a, -b), // z-
737         };
738         static const aabb3f glass_faces[6] = {
739                 aabb3f(-g, -g,  g,  g,  g,  g), // z+
740                 aabb3f(-g,  g, -g,  g,  g,  g), // y+
741                 aabb3f( g, -g, -g,  g,  g,  g), // x+
742                 aabb3f(-g, -g, -g,  g,  g, -g), // z-
743                 aabb3f(-g, -g, -g,  g, -g,  g), // y-
744                 aabb3f(-g, -g, -g, -g,  g,  g), // x-
745         };
746
747         // tables of neighbour (connect if same type and merge allowed),
748         // checked with g_26dirs
749
750         // 1 = connect, 0 = face visible
751         bool nb[FRAMED_NEIGHBOR_COUNT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
752
753         // 1 = check
754         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};
755         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};
756         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};
757         const bool *check_nb = check_nb_all;
758
759         // neighbours checks for frames visibility
760         if (H_merge || V_merge) {
761                 if (!H_merge)
762                         check_nb = check_nb_vertical; // vertical-only merge
763                 if (!V_merge)
764                         check_nb = check_nb_horizontal; // horizontal-only merge
765                 content_t current = n.getContent();
766                 for (int i = 0; i < FRAMED_NEIGHBOR_COUNT; i++) {
767                         if (!check_nb[i])
768                                 continue;
769                         v3s16 n2p = blockpos_nodes + p + g_26dirs[i];
770                         MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
771                         content_t n2c = n2.getContent();
772                         if (n2c == current)
773                                 nb[i] = 1;
774                 }
775         }
776
777         // edge visibility
778
779         static const u8 nb_triplet[FRAMED_EDGE_COUNT][3] = {
780                 {1, 2,  7}, {1, 5,  6}, {4, 2, 15}, {4, 5, 14},
781                 {2, 0, 11}, {2, 3, 13}, {5, 0, 10}, {5, 3, 12},
782                 {0, 1,  8}, {0, 4, 16}, {3, 4, 17}, {3, 1,  9},
783         };
784
785         tile = tiles[1];
786         for (int edge = 0; edge < FRAMED_EDGE_COUNT; edge++) {
787                 bool edge_invisible;
788                 if (nb[nb_triplet[edge][2]])
789                         edge_invisible = nb[nb_triplet[edge][0]] & nb[nb_triplet[edge][1]];
790                 else
791                         edge_invisible = nb[nb_triplet[edge][0]] ^ nb[nb_triplet[edge][1]];
792                 if (edge_invisible)
793                         continue;
794                 drawAutoLightedCuboid(frame_edges[edge]);
795         }
796
797         for (int face = 0; face < 6; face++) {
798                 if (nb[face])
799                         continue;
800                 tile = glass_tiles[face];
801                 drawAutoLightedCuboid(glass_faces[face]);
802         }
803
804         // Optionally render internal liquid level defined by param2
805         // Liquid is textured with 1 tile defined in nodedef 'special_tiles'
806         if (param2 > 0 && f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL &&
807                         f->special_tiles[0].layers[0].texture) {
808                 // Internal liquid level has param2 range 0 .. 63,
809                 // convert it to -0.5 .. 0.5
810                 float vlev = (param2 / 63.0) * 2.0 - 1.0;
811                 getSpecialTile(0, &tile);
812                 drawAutoLightedCuboid(aabb3f(-(nb[5] ? g : b),
813                                              -(nb[4] ? g : b),
814                                              -(nb[3] ? g : b),
815                                               (nb[2] ? g : b),
816                                               (nb[1] ? g : b) * vlev,
817                                               (nb[0] ? g : b)));
818         }
819 }
820
821 void MapblockMeshGenerator::drawAllfacesNode()
822 {
823         static const aabb3f box(-BS / 2, -BS / 2, -BS / 2, BS / 2, BS / 2, BS / 2);
824         useTile(0, 0, 0);
825         drawAutoLightedCuboid(box);
826 }
827
828 void MapblockMeshGenerator::drawTorchlikeNode()
829 {
830         u8 wall = n.getWallMounted(nodedef);
831         u8 tileindex = 0;
832         switch (wall) {
833                 case DWM_YP: tileindex = 1; break; // ceiling
834                 case DWM_YN: tileindex = 0; break; // floor
835                 default:     tileindex = 2; // side (or invalid—should we care?)
836         }
837         useTile(tileindex, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
838
839         float size = BS / 2 * f->visual_scale;
840         v3f vertices[4] = {
841                 v3f(-size,  size, 0),
842                 v3f( size,  size, 0),
843                 v3f( size, -size, 0),
844                 v3f(-size, -size, 0),
845         };
846
847         for (v3f &vertex : vertices) {
848                 switch (wall) {
849                         case DWM_YP:
850                                 vertex.rotateXZBy(-45); break;
851                         case DWM_YN:
852                                 vertex.rotateXZBy( 45); break;
853                         case DWM_XP:
854                                 vertex.rotateXZBy(  0); break;
855                         case DWM_XN:
856                                 vertex.rotateXZBy(180); break;
857                         case DWM_ZP:
858                                 vertex.rotateXZBy( 90); break;
859                         case DWM_ZN:
860                                 vertex.rotateXZBy(-90); break;
861                 }
862         }
863         drawQuad(vertices);
864 }
865
866 void MapblockMeshGenerator::drawSignlikeNode()
867 {
868         u8 wall = n.getWallMounted(nodedef);
869         useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
870         static const float offset = BS / 16;
871         float size = BS / 2 * f->visual_scale;
872         // Wall at X+ of node
873         v3f vertices[4] = {
874                 v3f(BS / 2 - offset,  size,  size),
875                 v3f(BS / 2 - offset,  size, -size),
876                 v3f(BS / 2 - offset, -size, -size),
877                 v3f(BS / 2 - offset, -size,  size),
878         };
879
880         for (v3f &vertex : vertices) {
881                 switch (wall) {
882                         case DWM_YP:
883                                 vertex.rotateXYBy( 90); break;
884                         case DWM_YN:
885                                 vertex.rotateXYBy(-90); break;
886                         case DWM_XP:
887                                 vertex.rotateXZBy(  0); break;
888                         case DWM_XN:
889                                 vertex.rotateXZBy(180); break;
890                         case DWM_ZP:
891                                 vertex.rotateXZBy( 90); break;
892                         case DWM_ZN:
893                                 vertex.rotateXZBy(-90); break;
894                 }
895         }
896         drawQuad(vertices);
897 }
898
899 void MapblockMeshGenerator::drawPlantlikeQuad(float rotation, float quad_offset,
900         bool offset_top_only)
901 {
902         v3f vertices[4] = {
903                 v3f(-scale, -BS / 2 + 2.0 * scale * plant_height, 0),
904                 v3f( scale, -BS / 2 + 2.0 * scale * plant_height, 0),
905                 v3f( scale, -BS / 2, 0),
906                 v3f(-scale, -BS / 2, 0),
907         };
908         if (random_offset_Y) {
909                 PseudoRandom yrng(face_num++ | p.X << 16 | p.Z << 8 | p.Y << 24);
910                 offset.Y = -BS * ((yrng.next() % 16 / 16.0) * 0.125);
911         }
912         int offset_count = offset_top_only ? 2 : 4;
913         for (int i = 0; i < offset_count; i++)
914                 vertices[i].Z += quad_offset;
915
916         for (v3f &vertex : vertices) {
917                 vertex.rotateXZBy(rotation + rotate_degree);
918                 vertex += offset;
919         }
920         drawQuad(vertices, v3s16(0, 0, 0), plant_height);
921 }
922
923 void MapblockMeshGenerator::drawPlantlike()
924 {
925         draw_style = PLANT_STYLE_CROSS;
926         scale = BS / 2 * f->visual_scale;
927         offset = v3f(0, 0, 0);
928         rotate_degree = 0;
929         random_offset_Y = false;
930         face_num = 0;
931         plant_height = 1.0;
932
933         switch (f->param_type_2) {
934         case CPT2_MESHOPTIONS:
935                 draw_style = PlantlikeStyle(n.param2 & MO_MASK_STYLE);
936                 if (n.param2 & MO_BIT_SCALE_SQRT2)
937                         scale *= 1.41421;
938                 if (n.param2 & MO_BIT_RANDOM_OFFSET) {
939                         PseudoRandom rng(p.X << 8 | p.Z | p.Y << 16);
940                         offset.X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
941                         offset.Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
942                 }
943                 if (n.param2 & MO_BIT_RANDOM_OFFSET_Y)
944                         random_offset_Y = true;
945                 break;
946
947         case CPT2_DEGROTATE:
948                 rotate_degree = n.param2 * 2;
949                 break;
950
951         case CPT2_LEVELED:
952                 plant_height = n.param2 / 16.0;
953                 break;
954
955         default:
956                 break;
957         }
958
959         switch (draw_style) {
960         case PLANT_STYLE_CROSS:
961                 drawPlantlikeQuad(46);
962                 drawPlantlikeQuad(-44);
963                 break;
964
965         case PLANT_STYLE_CROSS2:
966                 drawPlantlikeQuad(91);
967                 drawPlantlikeQuad(1);
968                 break;
969
970         case PLANT_STYLE_STAR:
971                 drawPlantlikeQuad(121);
972                 drawPlantlikeQuad(241);
973                 drawPlantlikeQuad(1);
974                 break;
975
976         case PLANT_STYLE_HASH:
977                 drawPlantlikeQuad(  1, BS / 4);
978                 drawPlantlikeQuad( 91, BS / 4);
979                 drawPlantlikeQuad(181, BS / 4);
980                 drawPlantlikeQuad(271, BS / 4);
981                 break;
982
983         case PLANT_STYLE_HASH2:
984                 drawPlantlikeQuad(  1, -BS / 2, true);
985                 drawPlantlikeQuad( 91, -BS / 2, true);
986                 drawPlantlikeQuad(181, -BS / 2, true);
987                 drawPlantlikeQuad(271, -BS / 2, true);
988                 break;
989         }
990 }
991
992 void MapblockMeshGenerator::drawPlantlikeNode()
993 {
994         useTile();
995         drawPlantlike();
996 }
997
998 void MapblockMeshGenerator::drawPlantlikeRootedNode()
999 {
1000         useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, 0, true);
1001         origin += v3f(0.0, BS, 0.0);
1002         p.Y++;
1003         if (data->m_smooth_lighting) {
1004                 getSmoothLightFrame();
1005         } else {
1006                 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1007                 light = LightPair(getInteriorLight(ntop, 1, nodedef));
1008         }
1009         drawPlantlike();
1010         p.Y--;
1011 }
1012
1013 void MapblockMeshGenerator::drawFirelikeQuad(float rotation, float opening_angle,
1014         float offset_h, float offset_v)
1015 {
1016         v3f vertices[4] = {
1017                 v3f(-scale, -BS / 2 + scale * 2, 0),
1018                 v3f( scale, -BS / 2 + scale * 2, 0),
1019                 v3f( scale, -BS / 2, 0),
1020                 v3f(-scale, -BS / 2, 0),
1021         };
1022
1023         for (v3f &vertex : vertices) {
1024                 vertex.rotateYZBy(opening_angle);
1025                 vertex.Z += offset_h;
1026                 vertex.rotateXZBy(rotation);
1027                 vertex.Y += offset_v;
1028         }
1029         drawQuad(vertices);
1030 }
1031
1032 void MapblockMeshGenerator::drawFirelikeNode()
1033 {
1034         useTile();
1035         scale = BS / 2 * f->visual_scale;
1036
1037         // Check for adjacent nodes
1038         bool neighbors = false;
1039         bool neighbor[6] = {0, 0, 0, 0, 0, 0};
1040         content_t current = n.getContent();
1041         for (int i = 0; i < 6; i++) {
1042                 v3s16 n2p = blockpos_nodes + p + g_6dirs[i];
1043                 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
1044                 content_t n2c = n2.getContent();
1045                 if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
1046                         neighbor[i] = true;
1047                         neighbors = true;
1048                 }
1049         }
1050         bool drawBasicFire = neighbor[D6D_YN] || !neighbors;
1051         bool drawBottomFire = neighbor[D6D_YP];
1052
1053         if (drawBasicFire || neighbor[D6D_ZP])
1054                 drawFirelikeQuad(0, -10, 0.4 * BS);
1055         else if (drawBottomFire)
1056                 drawFirelikeQuad(0, 70, 0.47 * BS, 0.484 * BS);
1057
1058         if (drawBasicFire || neighbor[D6D_XN])
1059                 drawFirelikeQuad(90, -10, 0.4 * BS);
1060         else if (drawBottomFire)
1061                 drawFirelikeQuad(90, 70, 0.47 * BS, 0.484 * BS);
1062
1063         if (drawBasicFire || neighbor[D6D_ZN])
1064                 drawFirelikeQuad(180, -10, 0.4 * BS);
1065         else if (drawBottomFire)
1066                 drawFirelikeQuad(180, 70, 0.47 * BS, 0.484 * BS);
1067
1068         if (drawBasicFire || neighbor[D6D_XP])
1069                 drawFirelikeQuad(270, -10, 0.4 * BS);
1070         else if (drawBottomFire)
1071                 drawFirelikeQuad(270, 70, 0.47 * BS, 0.484 * BS);
1072
1073         if (drawBasicFire) {
1074                 drawFirelikeQuad(45, 0, 0.0);
1075                 drawFirelikeQuad(-45, 0, 0.0);
1076         }
1077 }
1078
1079 void MapblockMeshGenerator::drawFencelikeNode()
1080 {
1081         useTile(0, 0, 0);
1082         TileSpec tile_nocrack = tile;
1083
1084         for (auto &layer : tile_nocrack.layers)
1085                 layer.material_flags &= ~MATERIAL_FLAG_CRACK;
1086
1087         // Put wood the right way around in the posts
1088         TileSpec tile_rot = tile;
1089         tile_rot.rotation = 1;
1090
1091         static const f32 post_rad = BS / 8;
1092         static const f32 bar_rad  = BS / 16;
1093         static const f32 bar_len  = BS / 2 - post_rad;
1094
1095         // The post - always present
1096         static const aabb3f post(-post_rad, -BS / 2, -post_rad,
1097                                   post_rad,  BS / 2,  post_rad);
1098         static const f32 postuv[24] = {
1099                 0.375, 0.375, 0.625, 0.625,
1100                 0.375, 0.375, 0.625, 0.625,
1101                 0.000, 0.000, 0.250, 1.000,
1102                 0.250, 0.000, 0.500, 1.000,
1103                 0.500, 0.000, 0.750, 1.000,
1104                 0.750, 0.000, 1.000, 1.000,
1105         };
1106         tile = tile_rot;
1107         drawAutoLightedCuboid(post, postuv);
1108
1109         tile = tile_nocrack;
1110
1111         // Now a section of fence, +X, if there's a post there
1112         v3s16 p2 = p;
1113         p2.X++;
1114         MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1115         const ContentFeatures *f2 = &nodedef->get(n2);
1116         if (f2->drawtype == NDT_FENCELIKE) {
1117                 static const aabb3f bar_x1(BS / 2 - bar_len,  BS / 4 - bar_rad, -bar_rad,
1118                                            BS / 2 + bar_len,  BS / 4 + bar_rad,  bar_rad);
1119                 static const aabb3f bar_x2(BS / 2 - bar_len, -BS / 4 - bar_rad, -bar_rad,
1120                                            BS / 2 + bar_len, -BS / 4 + bar_rad,  bar_rad);
1121                 static const f32 xrailuv[24] = {
1122                         0.000, 0.125, 1.000, 0.250,
1123                         0.000, 0.250, 1.000, 0.375,
1124                         0.375, 0.375, 0.500, 0.500,
1125                         0.625, 0.625, 0.750, 0.750,
1126                         0.000, 0.500, 1.000, 0.625,
1127                         0.000, 0.875, 1.000, 1.000,
1128                 };
1129                 drawAutoLightedCuboid(bar_x1, xrailuv);
1130                 drawAutoLightedCuboid(bar_x2, xrailuv);
1131         }
1132
1133         // Now a section of fence, +Z, if there's a post there
1134         p2 = p;
1135         p2.Z++;
1136         n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1137         f2 = &nodedef->get(n2);
1138         if (f2->drawtype == NDT_FENCELIKE) {
1139                 static const aabb3f bar_z1(-bar_rad,  BS / 4 - bar_rad, BS / 2 - bar_len,
1140                                             bar_rad,  BS / 4 + bar_rad, BS / 2 + bar_len);
1141                 static const aabb3f bar_z2(-bar_rad, -BS / 4 - bar_rad, BS / 2 - bar_len,
1142                                             bar_rad, -BS / 4 + bar_rad, BS / 2 + bar_len);
1143                 static const f32 zrailuv[24] = {
1144                         0.1875, 0.0625, 0.3125, 0.3125, // cannot rotate; stretch
1145                         0.2500, 0.0625, 0.3750, 0.3125, // for wood texture instead
1146                         0.0000, 0.5625, 1.0000, 0.6875,
1147                         0.0000, 0.3750, 1.0000, 0.5000,
1148                         0.3750, 0.3750, 0.5000, 0.5000,
1149                         0.6250, 0.6250, 0.7500, 0.7500,
1150                 };
1151                 drawAutoLightedCuboid(bar_z1, zrailuv);
1152                 drawAutoLightedCuboid(bar_z2, zrailuv);
1153         }
1154 }
1155
1156 bool MapblockMeshGenerator::isSameRail(v3s16 dir)
1157 {
1158         MapNode node2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
1159         if (node2.getContent() == n.getContent())
1160                 return true;
1161         const ContentFeatures &def2 = nodedef->get(node2);
1162         return ((def2.drawtype == NDT_RAILLIKE) &&
1163                 (def2.getGroup(raillike_groupname) == raillike_group));
1164 }
1165
1166 void MapblockMeshGenerator::drawRaillikeNode()
1167 {
1168         static const v3s16 direction[4] = {
1169                 v3s16( 0, 0,  1),
1170                 v3s16( 0, 0, -1),
1171                 v3s16(-1, 0,  0),
1172                 v3s16( 1, 0,  0),
1173         };
1174         static const int slope_angle[4] = {0, 180, 90, -90};
1175
1176         enum RailTile {
1177                 straight,
1178                 curved,
1179                 junction,
1180                 cross,
1181         };
1182         struct RailDesc {
1183                 int tile_index;
1184                 int angle;
1185         };
1186         static const RailDesc rail_kinds[16] = {
1187                                    // +x -x -z +z
1188                                    //-------------
1189                 {straight,   0}, //  .  .  .  .
1190                 {straight,   0}, //  .  .  . +Z
1191                 {straight,   0}, //  .  . -Z  .
1192                 {straight,   0}, //  .  . -Z +Z
1193                 {straight,  90}, //  . -X  .  .
1194                 {  curved, 180}, //  . -X  . +Z
1195                 {  curved, 270}, //  . -X -Z  .
1196                 {junction, 180}, //  . -X -Z +Z
1197                 {straight,  90}, // +X  .  .  .
1198                 {  curved,  90}, // +X  .  . +Z
1199                 {  curved,   0}, // +X  . -Z  .
1200                 {junction,   0}, // +X  . -Z +Z
1201                 {straight,  90}, // +X -X  .  .
1202                 {junction,  90}, // +X -X  . +Z
1203                 {junction, 270}, // +X -X -Z  .
1204                 {   cross,   0}, // +X -X -Z +Z
1205         };
1206
1207         raillike_group = nodedef->get(n).getGroup(raillike_groupname);
1208
1209         int code = 0;
1210         int angle;
1211         int tile_index;
1212         bool sloped = false;
1213         for (int dir = 0; dir < 4; dir++) {
1214                 bool rail_above = isSameRail(direction[dir] + v3s16(0, 1, 0));
1215                 if (rail_above) {
1216                         sloped = true;
1217                         angle = slope_angle[dir];
1218                 }
1219                 if (rail_above ||
1220                                 isSameRail(direction[dir]) ||
1221                                 isSameRail(direction[dir] + v3s16(0, -1, 0)))
1222                         code |= 1 << dir;
1223         }
1224
1225         if (sloped) {
1226                 tile_index = straight;
1227         } else {
1228                 tile_index = rail_kinds[code].tile_index;
1229                 angle = rail_kinds[code].angle;
1230         }
1231
1232         useTile(tile_index, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
1233
1234         static const float offset = BS / 64;
1235         static const float size   = BS / 2;
1236         float y2 = sloped ? size : -size;
1237         v3f vertices[4] = {
1238                 v3f(-size,    y2 + offset,  size),
1239                 v3f( size,    y2 + offset,  size),
1240                 v3f( size, -size + offset, -size),
1241                 v3f(-size, -size + offset, -size),
1242         };
1243         if (angle)
1244                 for (v3f &vertex : vertices)
1245                         vertex.rotateXZBy(angle);
1246         drawQuad(vertices);
1247 }
1248
1249 void MapblockMeshGenerator::drawNodeboxNode()
1250 {
1251         static const v3s16 tile_dirs[6] = {
1252                 v3s16(0, 1, 0),
1253                 v3s16(0, -1, 0),
1254                 v3s16(1, 0, 0),
1255                 v3s16(-1, 0, 0),
1256                 v3s16(0, 0, 1),
1257                 v3s16(0, 0, -1)
1258         };
1259
1260         // we have this order for some reason...
1261         static const v3s16 connection_dirs[6] = {
1262                 v3s16( 0,  1,  0), // top
1263                 v3s16( 0, -1,  0), // bottom
1264                 v3s16( 0,  0, -1), // front
1265                 v3s16(-1,  0,  0), // left
1266                 v3s16( 0,  0,  1), // back
1267                 v3s16( 1,  0,  0), // right
1268         };
1269
1270         TileSpec tiles[6];
1271         for (int face = 0; face < 6; face++) {
1272                 // Handles facedir rotation for textures
1273                 getTile(tile_dirs[face], &tiles[face]);
1274         }
1275
1276         // locate possible neighboring nodes to connect to
1277         int neighbors_set = 0;
1278         if (f->node_box.type == NODEBOX_CONNECTED) {
1279                 for (int dir = 0; dir != 6; dir++) {
1280                         int flag = 1 << dir;
1281                         v3s16 p2 = blockpos_nodes + p + connection_dirs[dir];
1282                         MapNode n2 = data->m_vmanip.getNodeNoEx(p2);
1283                         if (nodedef->nodeboxConnects(n, n2, flag))
1284                                 neighbors_set |= flag;
1285                 }
1286         }
1287
1288         std::vector<aabb3f> boxes;
1289         n.getNodeBoxes(nodedef, &boxes, neighbors_set);
1290         for (const auto &box : boxes)
1291                 drawAutoLightedCuboid(box, nullptr, tiles, 6);
1292 }
1293
1294 void MapblockMeshGenerator::drawMeshNode()
1295 {
1296         u8 facedir = 0;
1297         scene::IMesh* mesh;
1298         bool private_mesh; // as a grab/drop pair is not thread-safe
1299
1300         if (f->param_type_2 == CPT2_FACEDIR ||
1301                         f->param_type_2 == CPT2_COLORED_FACEDIR) {
1302                 facedir = n.getFaceDir(nodedef);
1303         } else if (f->param_type_2 == CPT2_WALLMOUNTED ||
1304                         f->param_type_2 == CPT2_COLORED_WALLMOUNTED) {
1305                 // Convert wallmounted to 6dfacedir.
1306                 // When cache enabled, it is already converted.
1307                 facedir = n.getWallMounted(nodedef);
1308                 if (!enable_mesh_cache)
1309                         facedir = wallmounted_to_facedir[facedir];
1310         }
1311
1312         if (!data->m_smooth_lighting && f->mesh_ptr[facedir]) {
1313                 // use cached meshes
1314                 private_mesh = false;
1315                 mesh = f->mesh_ptr[facedir];
1316         } else if (f->mesh_ptr[0]) {
1317                 // no cache, clone and rotate mesh
1318                 private_mesh = true;
1319                 mesh = cloneMesh(f->mesh_ptr[0]);
1320                 rotateMeshBy6dFacedir(mesh, facedir);
1321                 recalculateBoundingBox(mesh);
1322                 meshmanip->recalculateNormals(mesh, true, false);
1323         } else
1324                 return;
1325
1326         int mesh_buffer_count = mesh->getMeshBufferCount();
1327         for (int j = 0; j < mesh_buffer_count; j++) {
1328                 useTile(j);
1329                 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1330                 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1331                 int vertex_count = buf->getVertexCount();
1332
1333                 if (data->m_smooth_lighting) {
1334                         // Mesh is always private here. So the lighting is applied to each
1335                         // vertex right here.
1336                         for (int k = 0; k < vertex_count; k++) {
1337                                 video::S3DVertex &vertex = vertices[k];
1338                                 vertex.Color = blendLightColor(vertex.Pos, vertex.Normal);
1339                                 vertex.Pos += origin;
1340                         }
1341                         collector->append(tile, vertices, vertex_count,
1342                                 buf->getIndices(), buf->getIndexCount());
1343                 } else {
1344                         // Don't modify the mesh, it may not be private here.
1345                         // Instead, let the collector process colors, etc.
1346                         collector->append(tile, vertices, vertex_count,
1347                                 buf->getIndices(), buf->getIndexCount(), origin,
1348                                 color, f->light_source);
1349                 }
1350         }
1351         if (private_mesh)
1352                 mesh->drop();
1353 }
1354
1355 // also called when the drawtype is known but should have been pre-converted
1356 void MapblockMeshGenerator::errorUnknownDrawtype()
1357 {
1358         infostream << "Got drawtype " << f->drawtype << std::endl;
1359         FATAL_ERROR("Unknown drawtype");
1360 }
1361
1362 void MapblockMeshGenerator::drawNode()
1363 {
1364         // skip some drawtypes early
1365         switch (f->drawtype) {
1366                 case NDT_NORMAL:   // Drawn by MapBlockMesh
1367                 case NDT_AIRLIKE:  // Not drawn at all
1368                 case NDT_LIQUID:   // Drawn by MapBlockMesh
1369                         return;
1370                 default:
1371                         break;
1372         }
1373         origin = intToFloat(p, BS);
1374         if (data->m_smooth_lighting)
1375                 getSmoothLightFrame();
1376         else
1377                 light = LightPair(getInteriorLight(n, 1, nodedef));
1378         switch (f->drawtype) {
1379                 case NDT_FLOWINGLIQUID:     drawLiquidNode(); break;
1380                 case NDT_GLASSLIKE:         drawGlasslikeNode(); break;
1381                 case NDT_GLASSLIKE_FRAMED:  drawGlasslikeFramedNode(); break;
1382                 case NDT_ALLFACES:          drawAllfacesNode(); break;
1383                 case NDT_TORCHLIKE:         drawTorchlikeNode(); break;
1384                 case NDT_SIGNLIKE:          drawSignlikeNode(); break;
1385                 case NDT_PLANTLIKE:         drawPlantlikeNode(); break;
1386                 case NDT_PLANTLIKE_ROOTED:  drawPlantlikeRootedNode(); break;
1387                 case NDT_FIRELIKE:          drawFirelikeNode(); break;
1388                 case NDT_FENCELIKE:         drawFencelikeNode(); break;
1389                 case NDT_RAILLIKE:          drawRaillikeNode(); break;
1390                 case NDT_NODEBOX:           drawNodeboxNode(); break;
1391                 case NDT_MESH:              drawMeshNode(); break;
1392                 default:                    errorUnknownDrawtype(); break;
1393         }
1394 }
1395
1396 /*
1397         TODO: Fix alpha blending for special nodes
1398         Currently only the last element rendered is blended correct
1399 */
1400 void MapblockMeshGenerator::generate()
1401 {
1402         for (p.Z = 0; p.Z < MAP_BLOCKSIZE; p.Z++)
1403         for (p.Y = 0; p.Y < MAP_BLOCKSIZE; p.Y++)
1404         for (p.X = 0; p.X < MAP_BLOCKSIZE; p.X++) {
1405                 n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1406                 f = &nodedef->get(n);
1407                 drawNode();
1408         }
1409 }
1410
1411 void MapblockMeshGenerator::renderSingle(content_t node)
1412 {
1413         p = {0, 0, 0};
1414         n = MapNode(node, 0xff, 0x00);
1415         f = &nodedef->get(n);
1416         drawNode();
1417 }