3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
20 #include "content_mapblock.h"
21 #include "util/numeric.h"
22 #include "util/directiontables.h"
23 #include "mapblock_mesh.h"
26 #include "client/tile.h"
28 #include <IMeshManipulator.h>
29 #include "client/renderingengine.h"
33 // Distance of light extrapolation (for oversized nodes)
34 // After this distance, it gives up and considers light level constant
35 #define SMOOTH_LIGHTING_OVERSIZE 1.0
37 // Node edge count (for glasslike-framed)
38 #define FRAMED_EDGE_COUNT 12
40 // Node neighbor count, including edge-connected, but not vertex-connected
41 // (for glasslike-framed)
42 // Corresponding offsets are listed in g_27dirs
43 #define FRAMED_NEIGHBOR_COUNT 18
45 static const v3s16 light_dirs[8] = {
56 // Standard index set to make a quad on 4 vertices
57 static constexpr u16 quad_indices[] = {0, 1, 2, 2, 3, 0};
59 const std::string MapblockMeshGenerator::raillike_groupname = "connect_to_raillike";
61 MapblockMeshGenerator::MapblockMeshGenerator(MeshMakeData *input, MeshCollector *output)
66 nodedef = data->m_client->ndef();
67 meshmanip = RenderingEngine::get_scene_manager()->getMeshManipulator();
69 enable_mesh_cache = g_settings->getBool("enable_mesh_cache") &&
70 !data->m_smooth_lighting; // Mesh cache is not supported with smooth lighting
72 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
75 void MapblockMeshGenerator::useTile(int index, u8 set_flags, u8 reset_flags, bool special)
78 getSpecialTile(index, &tile, p == data->m_crack_pos_relative);
80 getNodeTileN(n, p, index, data, tile);
81 if (!data->m_smooth_lighting)
82 color = encode_light(light, f->light_source);
84 for (auto &layer : tile.layers) {
85 layer.material_flags |= set_flags;
86 layer.material_flags &= ~reset_flags;
90 void MapblockMeshGenerator::getTile(v3s16 direction, TileSpec *tile)
92 getNodeTile(n, p, direction, data, *tile);
96 * Returns the i-th special tile for a map node.
98 void MapblockMeshGenerator::getSpecialTile(int index, TileSpec *tile, bool apply_crack)
100 *tile = f->special_tiles[index];
101 TileLayer *top_layer = NULL;
103 for (auto &layernum : tile->layers) {
104 TileLayer *layer = &layernum;
105 if (layer->texture_id == 0)
108 if (!layer->has_color)
109 n.getColor(*f, &layer->color);
113 top_layer->material_flags |= MATERIAL_FLAG_CRACK;
116 void MapblockMeshGenerator::drawQuad(v3f *coords, const v3s16 &normal,
117 float vertical_tiling)
119 const v2f tcoords[4] = {v2f(0.0, 0.0), v2f(1.0, 0.0),
120 v2f(1.0, vertical_tiling), v2f(0.0, vertical_tiling)};
121 video::S3DVertex vertices[4];
122 bool shade_face = !f->light_source && (normal != v3s16(0, 0, 0));
123 v3f normal2(normal.X, normal.Y, normal.Z);
124 for (int j = 0; j < 4; j++) {
125 vertices[j].Pos = coords[j] + origin;
126 vertices[j].Normal = normal2;
127 if (data->m_smooth_lighting)
128 vertices[j].Color = blendLightColor(coords[j]);
130 vertices[j].Color = color;
132 applyFacesShading(vertices[j].Color, normal2);
133 vertices[j].TCoords = tcoords[j];
135 collector->append(tile, vertices, 4, quad_indices, 6);
139 // tiles - the tiles (materials) to use (for all 6 faces)
140 // tilecount - number of entries in tiles, 1<=tilecount<=6
141 // lights - vertex light levels. The order is the same as in light_dirs.
142 // NULL may be passed if smooth lighting is disabled.
143 // txc - texture coordinates - this is a list of texture coordinates
144 // for the opposite corners of each face - therefore, there
145 // should be (2+2)*6=24 values in the list. The order of
146 // the faces in the list is up-down-right-left-back-front
147 // (compatible with ContentFeatures).
148 void MapblockMeshGenerator::drawCuboid(const aabb3f &box,
149 TileSpec *tiles, int tilecount, const u16 *lights, const f32 *txc)
151 assert(tilecount >= 1 && tilecount <= 6); // pre-condition
153 v3f min = box.MinEdge;
154 v3f max = box.MaxEdge;
156 video::SColor colors[6];
157 if (!data->m_smooth_lighting) {
158 for (int face = 0; face != 6; ++face) {
159 colors[face] = encode_light(light, f->light_source);
161 if (!f->light_source) {
162 applyFacesShading(colors[0], v3f(0, 1, 0));
163 applyFacesShading(colors[1], v3f(0, -1, 0));
164 applyFacesShading(colors[2], v3f(1, 0, 0));
165 applyFacesShading(colors[3], v3f(-1, 0, 0));
166 applyFacesShading(colors[4], v3f(0, 0, 1));
167 applyFacesShading(colors[5], v3f(0, 0, -1));
171 video::S3DVertex vertices[24] = {
173 video::S3DVertex(min.X, max.Y, max.Z, 0, 1, 0, colors[0], txc[0], txc[1]),
174 video::S3DVertex(max.X, max.Y, max.Z, 0, 1, 0, colors[0], txc[2], txc[1]),
175 video::S3DVertex(max.X, max.Y, min.Z, 0, 1, 0, colors[0], txc[2], txc[3]),
176 video::S3DVertex(min.X, max.Y, min.Z, 0, 1, 0, colors[0], txc[0], txc[3]),
178 video::S3DVertex(min.X, min.Y, min.Z, 0, -1, 0, colors[1], txc[4], txc[5]),
179 video::S3DVertex(max.X, min.Y, min.Z, 0, -1, 0, colors[1], txc[6], txc[5]),
180 video::S3DVertex(max.X, min.Y, max.Z, 0, -1, 0, colors[1], txc[6], txc[7]),
181 video::S3DVertex(min.X, min.Y, max.Z, 0, -1, 0, colors[1], txc[4], txc[7]),
183 video::S3DVertex(max.X, max.Y, min.Z, 1, 0, 0, colors[2], txc[ 8], txc[9]),
184 video::S3DVertex(max.X, max.Y, max.Z, 1, 0, 0, colors[2], txc[10], txc[9]),
185 video::S3DVertex(max.X, min.Y, max.Z, 1, 0, 0, colors[2], txc[10], txc[11]),
186 video::S3DVertex(max.X, min.Y, min.Z, 1, 0, 0, colors[2], txc[ 8], txc[11]),
188 video::S3DVertex(min.X, max.Y, max.Z, -1, 0, 0, colors[3], txc[12], txc[13]),
189 video::S3DVertex(min.X, max.Y, min.Z, -1, 0, 0, colors[3], txc[14], txc[13]),
190 video::S3DVertex(min.X, min.Y, min.Z, -1, 0, 0, colors[3], txc[14], txc[15]),
191 video::S3DVertex(min.X, min.Y, max.Z, -1, 0, 0, colors[3], txc[12], txc[15]),
193 video::S3DVertex(max.X, max.Y, max.Z, 0, 0, 1, colors[4], txc[16], txc[17]),
194 video::S3DVertex(min.X, max.Y, max.Z, 0, 0, 1, colors[4], txc[18], txc[17]),
195 video::S3DVertex(min.X, min.Y, max.Z, 0, 0, 1, colors[4], txc[18], txc[19]),
196 video::S3DVertex(max.X, min.Y, max.Z, 0, 0, 1, colors[4], txc[16], txc[19]),
198 video::S3DVertex(min.X, max.Y, min.Z, 0, 0, -1, colors[5], txc[20], txc[21]),
199 video::S3DVertex(max.X, max.Y, min.Z, 0, 0, -1, colors[5], txc[22], txc[21]),
200 video::S3DVertex(max.X, min.Y, min.Z, 0, 0, -1, colors[5], txc[22], txc[23]),
201 video::S3DVertex(min.X, min.Y, min.Z, 0, 0, -1, colors[5], txc[20], txc[23]),
204 static const u8 light_indices[24] = {
213 for (int face = 0; face < 6; face++) {
214 int tileindex = MYMIN(face, tilecount - 1);
215 const TileSpec &tile = tiles[tileindex];
216 for (int j = 0; j < 4; j++) {
217 video::S3DVertex &vertex = vertices[face * 4 + j];
218 v2f &tcoords = vertex.TCoords;
219 switch (tile.rotation) {
223 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
226 tcoords.rotateBy(180, irr::core::vector2df(0, 0));
229 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
232 tcoords.X = 1.0 - tcoords.X;
233 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
236 tcoords.X = 1.0 - tcoords.X;
237 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
240 tcoords.Y = 1.0 - tcoords.Y;
241 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
244 tcoords.Y = 1.0 - tcoords.Y;
245 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
248 tcoords.X = 1.0 - tcoords.X;
251 tcoords.Y = 1.0 - tcoords.Y;
259 if (data->m_smooth_lighting) {
260 for (int j = 0; j < 24; ++j) {
261 vertices[j].Color = encode_light(lights[light_indices[j]],
263 if (!f->light_source)
264 applyFacesShading(vertices[j].Color, vertices[j].Normal);
268 // Add to mesh collector
269 for (int k = 0; k < 6; ++k) {
270 int tileindex = MYMIN(k, tilecount - 1);
271 collector->append(tiles[tileindex], vertices + 4 * k, 4, quad_indices, 6);
275 // Gets the base lighting values for a node
276 void MapblockMeshGenerator::getSmoothLightFrame()
278 for (int k = 0; k < 8; ++k) {
279 u16 light = getSmoothLight(blockpos_nodes + p, light_dirs[k], data);
280 frame.lightsA[k] = light & 0xff;
281 frame.lightsB[k] = light >> 8;
285 // Calculates vertex light level
286 // vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
287 u16 MapblockMeshGenerator::blendLight(const v3f &vertex_pos)
289 f32 x = core::clamp(vertex_pos.X / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
290 f32 y = core::clamp(vertex_pos.Y / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
291 f32 z = core::clamp(vertex_pos.Z / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
294 for (int k = 0; k < 8; ++k) {
295 f32 dx = (k & 4) ? x : 1 - x;
296 f32 dy = (k & 2) ? y : 1 - y;
297 f32 dz = (k & 1) ? z : 1 - z;
298 lightA += dx * dy * dz * frame.lightsA[k];
299 lightB += dx * dy * dz * frame.lightsB[k];
302 core::clamp(core::round32(lightA), 0, 255) |
303 core::clamp(core::round32(lightB), 0, 255) << 8;
306 // Calculates vertex color to be used in mapblock mesh
307 // vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
308 // tile_color - node's tile color
309 video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos)
311 u16 light = blendLight(vertex_pos);
312 return encode_light(light, f->light_source);
315 video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos,
316 const v3f &vertex_normal)
318 video::SColor color = blendLightColor(vertex_pos);
319 if (!f->light_source)
320 applyFacesShading(color, vertex_normal);
324 void MapblockMeshGenerator::generateCuboidTextureCoords(const aabb3f &box, f32 *coords)
326 f32 tx1 = (box.MinEdge.X / BS) + 0.5;
327 f32 ty1 = (box.MinEdge.Y / BS) + 0.5;
328 f32 tz1 = (box.MinEdge.Z / BS) + 0.5;
329 f32 tx2 = (box.MaxEdge.X / BS) + 0.5;
330 f32 ty2 = (box.MaxEdge.Y / BS) + 0.5;
331 f32 tz2 = (box.MaxEdge.Z / BS) + 0.5;
333 tx1, 1 - tz2, tx2, 1 - tz1, // up
334 tx1, tz1, tx2, tz2, // down
335 tz1, 1 - ty2, tz2, 1 - ty1, // right
336 1 - tz2, 1 - ty2, 1 - tz1, 1 - ty1, // left
337 1 - tx2, 1 - ty2, 1 - tx1, 1 - ty1, // back
338 tx1, 1 - ty2, tx2, 1 - ty1, // front
340 for (int i = 0; i != 24; ++i)
344 void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc,
345 TileSpec *tiles, int tile_count)
347 f32 texture_coord_buf[24];
348 f32 dx1 = box.MinEdge.X;
349 f32 dy1 = box.MinEdge.Y;
350 f32 dz1 = box.MinEdge.Z;
351 f32 dx2 = box.MaxEdge.X;
352 f32 dy2 = box.MaxEdge.Y;
353 f32 dz2 = box.MaxEdge.Z;
354 box.MinEdge += origin;
355 box.MaxEdge += origin;
357 generateCuboidTextureCoords(box, texture_coord_buf);
358 txc = texture_coord_buf;
364 if (data->m_smooth_lighting) {
366 for (int j = 0; j < 8; ++j) {
368 d.X = (j & 4) ? dx2 : dx1;
369 d.Y = (j & 2) ? dy2 : dy1;
370 d.Z = (j & 1) ? dz2 : dz1;
371 lights[j] = blendLight(d);
373 drawCuboid(box, tiles, tile_count, lights, txc);
375 drawCuboid(box, tiles, tile_count, NULL, txc);
379 void MapblockMeshGenerator::prepareLiquidNodeDrawing()
381 getSpecialTile(0, &tile_liquid_top);
382 getSpecialTile(1, &tile_liquid);
384 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y + 1, p.Z));
385 c_flowing = nodedef->getId(f->liquid_alternative_flowing);
386 c_source = nodedef->getId(f->liquid_alternative_source);
387 top_is_same_liquid = (ntop.getContent() == c_flowing) || (ntop.getContent() == c_source);
389 if (data->m_smooth_lighting)
390 return; // don't need to pre-compute anything in this case
392 if (f->light_source != 0) {
393 // If this liquid emits light and doesn't contain light, draw
394 // it at what it emits, for an increased effect
395 light = decode_light(f->light_source);
396 light = light | (light << 8);
397 } else if (nodedef->get(ntop).param_type == CPT_LIGHT) {
398 // Otherwise, use the light of the node on top if possible
399 light = getInteriorLight(ntop, 0, nodedef);
402 color_liquid_top = encode_light(light, f->light_source);
403 color = encode_light(light, f->light_source);
406 void MapblockMeshGenerator::getLiquidNeighborhood()
408 u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8);
410 for (int w = -1; w <= 1; w++)
411 for (int u = -1; u <= 1; u++) {
412 NeighborData &neighbor = liquid_neighbors[w + 1][u + 1];
413 v3s16 p2 = p + v3s16(u, 0, w);
414 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
415 neighbor.content = n2.getContent();
416 neighbor.level = -0.5 * BS;
417 neighbor.is_same_liquid = false;
418 neighbor.top_is_same_liquid = false;
420 if (neighbor.content == CONTENT_IGNORE)
423 if (neighbor.content == c_source) {
424 neighbor.is_same_liquid = true;
425 neighbor.level = 0.5 * BS;
426 } else if (neighbor.content == c_flowing) {
427 neighbor.is_same_liquid = true;
428 u8 liquid_level = (n2.param2 & LIQUID_LEVEL_MASK);
429 if (liquid_level <= LIQUID_LEVEL_MAX + 1 - range)
432 liquid_level -= (LIQUID_LEVEL_MAX + 1 - range);
433 neighbor.level = (-0.5 + (liquid_level + 0.5) / range) * BS;
436 // Check node above neighbor.
437 // NOTE: This doesn't get executed if neighbor
440 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
441 if (n2.getContent() == c_source || n2.getContent() == c_flowing)
442 neighbor.top_is_same_liquid = true;
446 void MapblockMeshGenerator::calculateCornerLevels()
448 for (int k = 0; k < 2; k++)
449 for (int i = 0; i < 2; i++)
450 corner_levels[k][i] = getCornerLevel(i, k);
453 f32 MapblockMeshGenerator::getCornerLevel(int i, int k)
458 for (int dk = 0; dk < 2; dk++)
459 for (int di = 0; di < 2; di++) {
460 NeighborData &neighbor_data = liquid_neighbors[k + dk][i + di];
461 content_t content = neighbor_data.content;
463 // If top is liquid, draw starting from top of node
464 if (neighbor_data.top_is_same_liquid)
467 // Source always has the full height
468 if (content == c_source)
471 // Flowing liquid has level information
472 if (content == c_flowing) {
473 sum += neighbor_data.level;
475 } else if (content == CONTENT_AIR) {
478 return -0.5 * BS + 0.2;
486 void MapblockMeshGenerator::drawLiquidSides()
488 struct LiquidFaceDesc {
490 v3s16 p[2]; // XZ only; 1 means +, 0 means -
495 static const LiquidFaceDesc base_faces[4] = {
496 {v3s16( 1, 0, 0), {v3s16(1, 0, 1), v3s16(1, 0, 0)}},
497 {v3s16(-1, 0, 0), {v3s16(0, 0, 0), v3s16(0, 0, 1)}},
498 {v3s16( 0, 0, 1), {v3s16(0, 0, 1), v3s16(1, 0, 1)}},
499 {v3s16( 0, 0, -1), {v3s16(1, 0, 0), v3s16(0, 0, 0)}},
501 static const UV base_vertices[4] = {
508 for (const auto &face : base_faces) {
509 const NeighborData &neighbor = liquid_neighbors[face.dir.Z + 1][face.dir.X + 1];
511 // No face between nodes of the same liquid, unless there is node
512 // at the top to which it should be connected. Again, unless the face
513 // there would be inside the liquid
514 if (neighbor.is_same_liquid) {
515 if (!top_is_same_liquid)
517 if (neighbor.top_is_same_liquid)
521 const ContentFeatures &neighbor_features = nodedef->get(neighbor.content);
522 // Don't draw face if neighbor is blocking the view
523 if (neighbor_features.solidness == 2)
526 video::S3DVertex vertices[4];
527 for (int j = 0; j < 4; j++) {
528 const UV &vertex = base_vertices[j];
529 const v3s16 &base = face.p[vertex.u];
531 pos.X = (base.X - 0.5) * BS;
532 pos.Z = (base.Z - 0.5) * BS;
534 pos.Y = neighbor.is_same_liquid ? corner_levels[base.Z][base.X] : -0.5 * BS;
536 pos.Y = !top_is_same_liquid ? corner_levels[base.Z][base.X] : 0.5 * BS;
537 if (data->m_smooth_lighting)
538 color = blendLightColor(pos);
540 vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, color, vertex.u, vertex.v);
542 collector->append(tile_liquid, vertices, 4, quad_indices, 6);
546 void MapblockMeshGenerator::drawLiquidTop()
548 // To get backface culling right, the vertices need to go
549 // clockwise around the front of the face. And we happened to
550 // calculate corner levels in exact reverse order.
551 static const int corner_resolve[4][2] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
553 video::S3DVertex vertices[4] = {
554 video::S3DVertex(-BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 0, 1),
555 video::S3DVertex( BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 1, 1),
556 video::S3DVertex( BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 1, 0),
557 video::S3DVertex(-BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 0, 0),
560 for (int i = 0; i < 4; i++) {
561 int u = corner_resolve[i][0];
562 int w = corner_resolve[i][1];
563 vertices[i].Pos.Y += corner_levels[w][u];
564 if (data->m_smooth_lighting)
565 vertices[i].Color = blendLightColor(vertices[i].Pos);
566 vertices[i].Pos += origin;
569 // Default downwards-flowing texture animation goes from
570 // -Z towards +Z, thus the direction is +Z.
571 // Rotate texture to make animation go in flow direction
572 // Positive if liquid moves towards +Z
573 f32 dz = (corner_levels[0][0] + corner_levels[0][1]) -
574 (corner_levels[1][0] + corner_levels[1][1]);
575 // Positive if liquid moves towards +X
576 f32 dx = (corner_levels[0][0] + corner_levels[1][0]) -
577 (corner_levels[0][1] + corner_levels[1][1]);
578 f32 tcoord_angle = atan2(dz, dx) * core::RADTODEG;
579 v2f tcoord_center(0.5, 0.5);
580 v2f tcoord_translate(blockpos_nodes.Z + p.Z, blockpos_nodes.X + p.X);
581 tcoord_translate.rotateBy(tcoord_angle);
582 tcoord_translate.X -= floor(tcoord_translate.X);
583 tcoord_translate.Y -= floor(tcoord_translate.Y);
585 for (auto &vertice : vertices) {
586 vertice.TCoords.rotateBy(tcoord_angle, tcoord_center);
587 vertice.TCoords += tcoord_translate;
590 std::swap(vertices[0].TCoords, vertices[2].TCoords);
592 collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
595 void MapblockMeshGenerator::drawLiquidNode()
597 prepareLiquidNodeDrawing();
598 getLiquidNeighborhood();
599 calculateCornerLevels();
601 if (!top_is_same_liquid)
605 void MapblockMeshGenerator::drawGlasslikeNode()
609 for (int face = 0; face < 6; face++) {
610 // Check this neighbor
611 v3s16 dir = g_6dirs[face];
612 v3s16 neighbor_pos = blockpos_nodes + p + dir;
613 MapNode neighbor = data->m_vmanip.getNodeNoExNoEmerge(neighbor_pos);
614 // Don't make face if neighbor is of same type
615 if (neighbor.getContent() == n.getContent())
619 v3f(-BS / 2, BS / 2, -BS / 2),
620 v3f( BS / 2, BS / 2, -BS / 2),
621 v3f( BS / 2, -BS / 2, -BS / 2),
622 v3f(-BS / 2, -BS / 2, -BS / 2),
625 for (auto &vertice : vertices) {
628 vertice.rotateXZBy(180); break;
630 vertice.rotateYZBy( 90); break;
632 vertice.rotateXZBy( 90); break;
634 vertice.rotateXZBy( 0); break;
636 vertice.rotateYZBy(-90); break;
638 vertice.rotateXZBy(-90); break;
641 drawQuad(vertices, dir);
645 void MapblockMeshGenerator::drawGlasslikeFramedNode()
648 for (int face = 0; face < 6; face++)
649 getTile(g_6dirs[face], &tiles[face]);
651 TileSpec glass_tiles[6];
652 if (tiles[1].layers[0].texture &&
653 tiles[2].layers[0].texture &&
654 tiles[3].layers[0].texture) {
655 glass_tiles[0] = tiles[4];
656 glass_tiles[1] = tiles[0];
657 glass_tiles[2] = tiles[4];
658 glass_tiles[3] = tiles[4];
659 glass_tiles[4] = tiles[3];
660 glass_tiles[5] = tiles[4];
662 for (auto &glass_tile : glass_tiles)
663 glass_tile = tiles[4];
666 u8 param2 = n.getParam2();
667 bool H_merge = !(param2 & 128);
668 bool V_merge = !(param2 & 64);
671 static const float a = BS / 2;
672 static const float g = a - 0.003;
673 static const float b = .876 * ( BS / 2 );
675 static const aabb3f frame_edges[FRAMED_EDGE_COUNT] = {
676 aabb3f( b, b, -a, a, a, a), // y+
677 aabb3f(-a, b, -a, -b, a, a), // y+
678 aabb3f( b, -a, -a, a, -b, a), // y-
679 aabb3f(-a, -a, -a, -b, -b, a), // y-
680 aabb3f( b, -a, b, a, a, a), // x+
681 aabb3f( b, -a, -a, a, a, -b), // x+
682 aabb3f(-a, -a, b, -b, a, a), // x-
683 aabb3f(-a, -a, -a, -b, a, -b), // x-
684 aabb3f(-a, b, b, a, a, a), // z+
685 aabb3f(-a, -a, b, a, -b, a), // z+
686 aabb3f(-a, -a, -a, a, -b, -b), // z-
687 aabb3f(-a, b, -a, a, a, -b), // z-
689 static const aabb3f glass_faces[6] = {
690 aabb3f(-g, -g, g, g, g, g), // z+
691 aabb3f(-g, g, -g, g, g, g), // y+
692 aabb3f( g, -g, -g, g, g, g), // x+
693 aabb3f(-g, -g, -g, g, g, -g), // z-
694 aabb3f(-g, -g, -g, g, -g, g), // y-
695 aabb3f(-g, -g, -g, -g, g, g), // x-
698 // tables of neighbour (connect if same type and merge allowed),
699 // checked with g_26dirs
701 // 1 = connect, 0 = face visible
702 bool nb[FRAMED_NEIGHBOR_COUNT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
705 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};
706 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};
707 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};
708 const bool *check_nb = check_nb_all;
710 // neighbours checks for frames visibility
711 if (H_merge || V_merge) {
713 check_nb = check_nb_vertical; // vertical-only merge
715 check_nb = check_nb_horizontal; // horizontal-only merge
716 content_t current = n.getContent();
717 for (int i = 0; i < FRAMED_NEIGHBOR_COUNT; i++) {
720 v3s16 n2p = blockpos_nodes + p + g_26dirs[i];
721 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
722 content_t n2c = n2.getContent();
723 if (n2c == current || n2c == CONTENT_IGNORE)
730 static const u8 nb_triplet[FRAMED_EDGE_COUNT][3] = {
731 {1, 2, 7}, {1, 5, 6}, {4, 2, 15}, {4, 5, 14},
732 {2, 0, 11}, {2, 3, 13}, {5, 0, 10}, {5, 3, 12},
733 {0, 1, 8}, {0, 4, 16}, {3, 4, 17}, {3, 1, 9},
737 for (int edge = 0; edge < FRAMED_EDGE_COUNT; edge++) {
739 if (nb[nb_triplet[edge][2]])
740 edge_invisible = nb[nb_triplet[edge][0]] & nb[nb_triplet[edge][1]];
742 edge_invisible = nb[nb_triplet[edge][0]] ^ nb[nb_triplet[edge][1]];
745 drawAutoLightedCuboid(frame_edges[edge]);
748 for (int face = 0; face < 6; face++) {
751 tile = glass_tiles[face];
752 drawAutoLightedCuboid(glass_faces[face]);
755 // Optionally render internal liquid level defined by param2
756 // Liquid is textured with 1 tile defined in nodedef 'special_tiles'
757 if (param2 > 0 && f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL &&
758 f->special_tiles[0].layers[0].texture) {
759 // Internal liquid level has param2 range 0 .. 63,
760 // convert it to -0.5 .. 0.5
761 float vlev = (param2 / 63.0) * 2.0 - 1.0;
762 getSpecialTile(0, &tile);
763 drawAutoLightedCuboid(aabb3f(-(nb[5] ? g : b),
767 (nb[1] ? g : b) * vlev,
772 void MapblockMeshGenerator::drawAllfacesNode()
774 static const aabb3f box(-BS / 2, -BS / 2, -BS / 2, BS / 2, BS / 2, BS / 2);
776 drawAutoLightedCuboid(box);
779 void MapblockMeshGenerator::drawTorchlikeNode()
781 u8 wall = n.getWallMounted(nodedef);
784 case DWM_YP: tileindex = 1; break; // ceiling
785 case DWM_YN: tileindex = 0; break; // floor
786 default: tileindex = 2; // side (or invalid—should we care?)
788 useTile(tileindex, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
790 float size = BS / 2 * f->visual_scale;
794 v3f( size, -size, 0),
795 v3f(-size, -size, 0),
798 for (auto &vertice : vertices) {
801 vertice.rotateXZBy(-45); break;
803 vertice.rotateXZBy( 45); break;
805 vertice.rotateXZBy( 0); break;
807 vertice.rotateXZBy(180); break;
809 vertice.rotateXZBy( 90); break;
811 vertice.rotateXZBy(-90); break;
817 void MapblockMeshGenerator::drawSignlikeNode()
819 u8 wall = n.getWallMounted(nodedef);
820 useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
821 static const float offset = BS / 16;
822 float size = BS / 2 * f->visual_scale;
823 // Wall at X+ of node
825 v3f(BS / 2 - offset, size, size),
826 v3f(BS / 2 - offset, size, -size),
827 v3f(BS / 2 - offset, -size, -size),
828 v3f(BS / 2 - offset, -size, size),
831 for (auto &vertice : vertices) {
834 vertice.rotateXYBy( 90); break;
836 vertice.rotateXYBy(-90); break;
838 vertice.rotateXZBy( 0); break;
840 vertice.rotateXZBy(180); break;
842 vertice.rotateXZBy( 90); break;
844 vertice.rotateXZBy(-90); break;
850 void MapblockMeshGenerator::drawPlantlikeQuad(float rotation, float quad_offset,
851 bool offset_top_only)
854 v3f(-scale, -BS / 2 + 2.0 * scale * plant_height, 0),
855 v3f( scale, -BS / 2 + 2.0 * scale * plant_height, 0),
856 v3f( scale, -BS / 2, 0),
857 v3f(-scale, -BS / 2, 0),
859 if (random_offset_Y) {
860 PseudoRandom yrng(face_num++ | p.X << 16 | p.Z << 8 | p.Y << 24);
861 offset.Y = -BS * ((yrng.next() % 16 / 16.0) * 0.125);
863 int offset_count = offset_top_only ? 2 : 4;
864 for (int i = 0; i < offset_count; i++)
865 vertices[i].Z += quad_offset;
867 for (auto &vertice : vertices) {
868 vertice.rotateXZBy(rotation + rotate_degree);
871 drawQuad(vertices, v3s16(0, 0, 0), plant_height);
874 void MapblockMeshGenerator::drawPlantlike()
876 draw_style = PLANT_STYLE_CROSS;
877 scale = BS / 2 * f->visual_scale;
878 offset = v3f(0, 0, 0);
880 random_offset_Y = false;
884 switch (f->param_type_2) {
885 case CPT2_MESHOPTIONS:
886 draw_style = PlantlikeStyle(n.param2 & MO_MASK_STYLE);
887 if (n.param2 & MO_BIT_SCALE_SQRT2)
889 if (n.param2 & MO_BIT_RANDOM_OFFSET) {
890 PseudoRandom rng(p.X << 8 | p.Z | p.Y << 16);
891 offset.X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
892 offset.Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
894 if (n.param2 & MO_BIT_RANDOM_OFFSET_Y)
895 random_offset_Y = true;
899 rotate_degree = n.param2 * 2;
903 plant_height = n.param2 / 16.0;
910 switch (draw_style) {
911 case PLANT_STYLE_CROSS:
912 drawPlantlikeQuad(46);
913 drawPlantlikeQuad(-44);
916 case PLANT_STYLE_CROSS2:
917 drawPlantlikeQuad(91);
918 drawPlantlikeQuad(1);
921 case PLANT_STYLE_STAR:
922 drawPlantlikeQuad(121);
923 drawPlantlikeQuad(241);
924 drawPlantlikeQuad(1);
927 case PLANT_STYLE_HASH:
928 drawPlantlikeQuad( 1, BS / 4);
929 drawPlantlikeQuad( 91, BS / 4);
930 drawPlantlikeQuad(181, BS / 4);
931 drawPlantlikeQuad(271, BS / 4);
934 case PLANT_STYLE_HASH2:
935 drawPlantlikeQuad( 1, -BS / 2, true);
936 drawPlantlikeQuad( 91, -BS / 2, true);
937 drawPlantlikeQuad(181, -BS / 2, true);
938 drawPlantlikeQuad(271, -BS / 2, true);
943 void MapblockMeshGenerator::drawPlantlikeNode()
949 void MapblockMeshGenerator::drawPlantlikeRootedNode()
951 useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, 0, true);
952 origin += v3f(0.0, BS, 0.0);
954 if (data->m_smooth_lighting) {
955 getSmoothLightFrame();
957 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
958 light = getInteriorLight(ntop, 1, nodedef);
964 void MapblockMeshGenerator::drawFirelikeQuad(float rotation, float opening_angle,
965 float offset_h, float offset_v)
968 v3f(-scale, -BS / 2 + scale * 2, 0),
969 v3f( scale, -BS / 2 + scale * 2, 0),
970 v3f( scale, -BS / 2, 0),
971 v3f(-scale, -BS / 2, 0),
974 for (auto &vertice : vertices) {
975 vertice.rotateYZBy(opening_angle);
976 vertice.Z += offset_h;
977 vertice.rotateXZBy(rotation);
978 vertice.Y += offset_v;
983 void MapblockMeshGenerator::drawFirelikeNode()
986 scale = BS / 2 * f->visual_scale;
988 // Check for adjacent nodes
989 bool neighbors = false;
990 bool neighbor[6] = {0, 0, 0, 0, 0, 0};
991 content_t current = n.getContent();
992 for (int i = 0; i < 6; i++) {
993 v3s16 n2p = blockpos_nodes + p + g_6dirs[i];
994 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
995 content_t n2c = n2.getContent();
996 if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
1001 bool drawBasicFire = neighbor[D6D_YN] || !neighbors;
1002 bool drawBottomFire = neighbor[D6D_YP];
1004 if (drawBasicFire || neighbor[D6D_ZP])
1005 drawFirelikeQuad(0, -10, 0.4 * BS);
1006 else if (drawBottomFire)
1007 drawFirelikeQuad(0, 70, 0.47 * BS, 0.484 * BS);
1009 if (drawBasicFire || neighbor[D6D_XN])
1010 drawFirelikeQuad(90, -10, 0.4 * BS);
1011 else if (drawBottomFire)
1012 drawFirelikeQuad(90, 70, 0.47 * BS, 0.484 * BS);
1014 if (drawBasicFire || neighbor[D6D_ZN])
1015 drawFirelikeQuad(180, -10, 0.4 * BS);
1016 else if (drawBottomFire)
1017 drawFirelikeQuad(180, 70, 0.47 * BS, 0.484 * BS);
1019 if (drawBasicFire || neighbor[D6D_XP])
1020 drawFirelikeQuad(270, -10, 0.4 * BS);
1021 else if (drawBottomFire)
1022 drawFirelikeQuad(270, 70, 0.47 * BS, 0.484 * BS);
1024 if (drawBasicFire) {
1025 drawFirelikeQuad(45, 0, 0.0);
1026 drawFirelikeQuad(-45, 0, 0.0);
1030 void MapblockMeshGenerator::drawFencelikeNode()
1033 TileSpec tile_nocrack = tile;
1035 for (auto &layer : tile_nocrack.layers)
1036 layer.material_flags &= ~MATERIAL_FLAG_CRACK;
1038 // Put wood the right way around in the posts
1039 TileSpec tile_rot = tile;
1040 tile_rot.rotation = 1;
1042 static const f32 post_rad = BS / 8;
1043 static const f32 bar_rad = BS / 16;
1044 static const f32 bar_len = BS / 2 - post_rad;
1046 // The post - always present
1047 static const aabb3f post(-post_rad, -BS / 2, -post_rad,
1048 post_rad, BS / 2, post_rad);
1049 static const f32 postuv[24] = {
1050 0.375, 0.375, 0.625, 0.625,
1051 0.375, 0.375, 0.625, 0.625,
1052 0.000, 0.000, 0.250, 1.000,
1053 0.250, 0.000, 0.500, 1.000,
1054 0.500, 0.000, 0.750, 1.000,
1055 0.750, 0.000, 1.000, 1.000,
1058 drawAutoLightedCuboid(post, postuv);
1060 tile = tile_nocrack;
1062 // Now a section of fence, +X, if there's a post there
1065 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1066 const ContentFeatures *f2 = &nodedef->get(n2);
1067 if (f2->drawtype == NDT_FENCELIKE) {
1068 static const aabb3f bar_x1(BS / 2 - bar_len, BS / 4 - bar_rad, -bar_rad,
1069 BS / 2 + bar_len, BS / 4 + bar_rad, bar_rad);
1070 static const aabb3f bar_x2(BS / 2 - bar_len, -BS / 4 - bar_rad, -bar_rad,
1071 BS / 2 + bar_len, -BS / 4 + bar_rad, bar_rad);
1072 static const f32 xrailuv[24] = {
1073 0.000, 0.125, 1.000, 0.250,
1074 0.000, 0.250, 1.000, 0.375,
1075 0.375, 0.375, 0.500, 0.500,
1076 0.625, 0.625, 0.750, 0.750,
1077 0.000, 0.500, 1.000, 0.625,
1078 0.000, 0.875, 1.000, 1.000,
1080 drawAutoLightedCuboid(bar_x1, xrailuv);
1081 drawAutoLightedCuboid(bar_x2, xrailuv);
1084 // Now a section of fence, +Z, if there's a post there
1087 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1088 f2 = &nodedef->get(n2);
1089 if (f2->drawtype == NDT_FENCELIKE) {
1090 static const aabb3f bar_z1(-bar_rad, BS / 4 - bar_rad, BS / 2 - bar_len,
1091 bar_rad, BS / 4 + bar_rad, BS / 2 + bar_len);
1092 static const aabb3f bar_z2(-bar_rad, -BS / 4 - bar_rad, BS / 2 - bar_len,
1093 bar_rad, -BS / 4 + bar_rad, BS / 2 + bar_len);
1094 static const f32 zrailuv[24] = {
1095 0.1875, 0.0625, 0.3125, 0.3125, // cannot rotate; stretch
1096 0.2500, 0.0625, 0.3750, 0.3125, // for wood texture instead
1097 0.0000, 0.5625, 1.0000, 0.6875,
1098 0.0000, 0.3750, 1.0000, 0.5000,
1099 0.3750, 0.3750, 0.5000, 0.5000,
1100 0.6250, 0.6250, 0.7500, 0.7500,
1102 drawAutoLightedCuboid(bar_z1, zrailuv);
1103 drawAutoLightedCuboid(bar_z2, zrailuv);
1107 bool MapblockMeshGenerator::isSameRail(v3s16 dir)
1109 MapNode node2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
1110 if (node2.getContent() == n.getContent())
1112 const ContentFeatures &def2 = nodedef->get(node2);
1113 return ((def2.drawtype == NDT_RAILLIKE) &&
1114 (def2.getGroup(raillike_groupname) == raillike_group));
1117 void MapblockMeshGenerator::drawRaillikeNode()
1119 static const v3s16 direction[4] = {
1125 static const int slope_angle[4] = {0, 180, 90, -90};
1137 static const RailDesc rail_kinds[16] = {
1140 {straight, 0}, // . . . .
1141 {straight, 0}, // . . . +Z
1142 {straight, 0}, // . . -Z .
1143 {straight, 0}, // . . -Z +Z
1144 {straight, 90}, // . -X . .
1145 { curved, 180}, // . -X . +Z
1146 { curved, 270}, // . -X -Z .
1147 {junction, 180}, // . -X -Z +Z
1148 {straight, 90}, // +X . . .
1149 { curved, 90}, // +X . . +Z
1150 { curved, 0}, // +X . -Z .
1151 {junction, 0}, // +X . -Z +Z
1152 {straight, 90}, // +X -X . .
1153 {junction, 90}, // +X -X . +Z
1154 {junction, 270}, // +X -X -Z .
1155 { cross, 0}, // +X -X -Z +Z
1158 raillike_group = nodedef->get(n).getGroup(raillike_groupname);
1163 bool sloped = false;
1164 for (int dir = 0; dir < 4; dir++) {
1165 bool rail_above = isSameRail(direction[dir] + v3s16(0, 1, 0));
1168 angle = slope_angle[dir];
1171 isSameRail(direction[dir]) ||
1172 isSameRail(direction[dir] + v3s16(0, -1, 0)))
1177 tile_index = straight;
1179 tile_index = rail_kinds[code].tile_index;
1180 angle = rail_kinds[code].angle;
1183 useTile(tile_index, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
1185 static const float offset = BS / 64;
1186 static const float size = BS / 2;
1187 float y2 = sloped ? size : -size;
1189 v3f(-size, y2 + offset, size),
1190 v3f( size, y2 + offset, size),
1191 v3f( size, -size + offset, -size),
1192 v3f(-size, -size + offset, -size),
1195 for (auto &vertice : vertices)
1196 vertice.rotateXZBy(angle);
1200 void MapblockMeshGenerator::drawNodeboxNode()
1202 static const v3s16 tile_dirs[6] = {
1211 // we have this order for some reason...
1212 static const v3s16 connection_dirs[6] = {
1213 v3s16( 0, 1, 0), // top
1214 v3s16( 0, -1, 0), // bottom
1215 v3s16( 0, 0, -1), // front
1216 v3s16(-1, 0, 0), // left
1217 v3s16( 0, 0, 1), // back
1218 v3s16( 1, 0, 0), // right
1222 for (int face = 0; face < 6; face++) {
1223 // Handles facedir rotation for textures
1224 getTile(tile_dirs[face], &tiles[face]);
1227 // locate possible neighboring nodes to connect to
1228 int neighbors_set = 0;
1229 if (f->node_box.type == NODEBOX_CONNECTED) {
1230 for (int dir = 0; dir != 6; dir++) {
1231 int flag = 1 << dir;
1232 v3s16 p2 = blockpos_nodes + p + connection_dirs[dir];
1233 MapNode n2 = data->m_vmanip.getNodeNoEx(p2);
1234 if (nodedef->nodeboxConnects(n, n2, flag))
1235 neighbors_set |= flag;
1239 std::vector<aabb3f> boxes;
1240 n.getNodeBoxes(nodedef, &boxes, neighbors_set);
1241 for (const auto &box : boxes)
1242 drawAutoLightedCuboid(box, NULL, tiles, 6);
1245 void MapblockMeshGenerator::drawMeshNode()
1249 bool private_mesh; // as a grab/drop pair is not thread-safe
1251 if (f->param_type_2 == CPT2_FACEDIR ||
1252 f->param_type_2 == CPT2_COLORED_FACEDIR) {
1253 facedir = n.getFaceDir(nodedef);
1254 } else if (f->param_type_2 == CPT2_WALLMOUNTED ||
1255 f->param_type_2 == CPT2_COLORED_WALLMOUNTED) {
1256 // Convert wallmounted to 6dfacedir.
1257 // When cache enabled, it is already converted.
1258 facedir = n.getWallMounted(nodedef);
1259 if (!enable_mesh_cache) {
1260 static const u8 wm_to_6d[6] = {20, 0, 16 + 1, 12 + 3, 8, 4 + 2};
1261 facedir = wm_to_6d[facedir];
1265 if (!data->m_smooth_lighting && f->mesh_ptr[facedir]) {
1266 // use cached meshes
1267 private_mesh = false;
1268 mesh = f->mesh_ptr[facedir];
1269 } else if (f->mesh_ptr[0]) {
1270 // no cache, clone and rotate mesh
1271 private_mesh = true;
1272 mesh = cloneMesh(f->mesh_ptr[0]);
1273 rotateMeshBy6dFacedir(mesh, facedir);
1274 recalculateBoundingBox(mesh);
1275 meshmanip->recalculateNormals(mesh, true, false);
1279 int mesh_buffer_count = mesh->getMeshBufferCount();
1280 for (int j = 0; j < mesh_buffer_count; j++) {
1282 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1283 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1284 int vertex_count = buf->getVertexCount();
1286 if (data->m_smooth_lighting) {
1287 // Mesh is always private here. So the lighting is applied to each
1288 // vertex right here.
1289 for (int k = 0; k < vertex_count; k++) {
1290 video::S3DVertex &vertex = vertices[k];
1291 vertex.Color = blendLightColor(vertex.Pos, vertex.Normal);
1292 vertex.Pos += origin;
1294 collector->append(tile, vertices, vertex_count,
1295 buf->getIndices(), buf->getIndexCount());
1297 // Don't modify the mesh, it may not be private here.
1298 // Instead, let the collector process colors, etc.
1299 collector->append(tile, vertices, vertex_count,
1300 buf->getIndices(), buf->getIndexCount(), origin,
1301 color, f->light_source);
1308 // also called when the drawtype is known but should have been pre-converted
1309 void MapblockMeshGenerator::errorUnknownDrawtype()
1311 infostream << "Got drawtype " << f->drawtype << std::endl;
1312 FATAL_ERROR("Unknown drawtype");
1315 void MapblockMeshGenerator::drawNode()
1317 // skip some drawtypes early
1318 switch (f->drawtype) {
1319 case NDT_NORMAL: // Drawn by MapBlockMesh
1320 case NDT_AIRLIKE: // Not drawn at all
1321 case NDT_LIQUID: // Drawn by MapBlockMesh
1326 origin = intToFloat(p, BS);
1327 if (data->m_smooth_lighting)
1328 getSmoothLightFrame();
1330 light = getInteriorLight(n, 1, nodedef);
1331 switch (f->drawtype) {
1332 case NDT_FLOWINGLIQUID: drawLiquidNode(); break;
1333 case NDT_GLASSLIKE: drawGlasslikeNode(); break;
1334 case NDT_GLASSLIKE_FRAMED: drawGlasslikeFramedNode(); break;
1335 case NDT_ALLFACES: drawAllfacesNode(); break;
1336 case NDT_TORCHLIKE: drawTorchlikeNode(); break;
1337 case NDT_SIGNLIKE: drawSignlikeNode(); break;
1338 case NDT_PLANTLIKE: drawPlantlikeNode(); break;
1339 case NDT_PLANTLIKE_ROOTED: drawPlantlikeRootedNode(); break;
1340 case NDT_FIRELIKE: drawFirelikeNode(); break;
1341 case NDT_FENCELIKE: drawFencelikeNode(); break;
1342 case NDT_RAILLIKE: drawRaillikeNode(); break;
1343 case NDT_NODEBOX: drawNodeboxNode(); break;
1344 case NDT_MESH: drawMeshNode(); break;
1345 default: errorUnknownDrawtype(); break;
1350 TODO: Fix alpha blending for special nodes
1351 Currently only the last element rendered is blended correct
1353 void MapblockMeshGenerator::generate()
1355 for (p.Z = 0; p.Z < MAP_BLOCKSIZE; p.Z++)
1356 for (p.Y = 0; p.Y < MAP_BLOCKSIZE; p.Y++)
1357 for (p.X = 0; p.X < MAP_BLOCKSIZE; p.X++) {
1358 n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1359 f = &nodedef->get(n);