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 getTile(index, &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 // Returns a tile, ready for use, non-rotated.
91 void MapblockMeshGenerator::getTile(int index, TileSpec *tile)
93 getNodeTileN(n, p, index, data, *tile);
96 // Returns a tile, ready for use, rotated according to the node facedir.
97 void MapblockMeshGenerator::getTile(v3s16 direction, TileSpec *tile)
99 getNodeTile(n, p, direction, data, *tile);
102 // Returns a special tile, ready for use, non-rotated.
103 void MapblockMeshGenerator::getSpecialTile(int index, TileSpec *tile, bool apply_crack)
105 *tile = f->special_tiles[index];
106 TileLayer *top_layer = nullptr;
108 for (auto &layernum : tile->layers) {
109 TileLayer *layer = &layernum;
110 if (layer->texture_id == 0)
113 if (!layer->has_color)
114 n.getColor(*f, &layer->color);
118 top_layer->material_flags |= MATERIAL_FLAG_CRACK;
121 void MapblockMeshGenerator::drawQuad(v3f *coords, const v3s16 &normal,
122 float vertical_tiling)
124 const v2f tcoords[4] = {v2f(0.0, 0.0), v2f(1.0, 0.0),
125 v2f(1.0, vertical_tiling), v2f(0.0, vertical_tiling)};
126 video::S3DVertex vertices[4];
127 bool shade_face = !f->light_source && (normal != v3s16(0, 0, 0));
128 v3f normal2(normal.X, normal.Y, normal.Z);
129 for (int j = 0; j < 4; j++) {
130 vertices[j].Pos = coords[j] + origin;
131 vertices[j].Normal = normal2;
132 if (data->m_smooth_lighting)
133 vertices[j].Color = blendLightColor(coords[j]);
135 vertices[j].Color = color;
137 applyFacesShading(vertices[j].Color, normal2);
138 vertices[j].TCoords = tcoords[j];
140 collector->append(tile, vertices, 4, quad_indices, 6);
144 // tiles - the tiles (materials) to use (for all 6 faces)
145 // tilecount - number of entries in tiles, 1<=tilecount<=6
146 // lights - vertex light levels. The order is the same as in light_dirs.
147 // NULL may be passed if smooth lighting is disabled.
148 // txc - texture coordinates - this is a list of texture coordinates
149 // for the opposite corners of each face - therefore, there
150 // should be (2+2)*6=24 values in the list. The order of
151 // the faces in the list is up-down-right-left-back-front
152 // (compatible with ContentFeatures).
153 void MapblockMeshGenerator::drawCuboid(const aabb3f &box,
154 TileSpec *tiles, int tilecount, const LightPair *lights, const f32 *txc)
156 assert(tilecount >= 1 && tilecount <= 6); // pre-condition
158 v3f min = box.MinEdge;
159 v3f max = box.MaxEdge;
161 video::SColor colors[6];
162 if (!data->m_smooth_lighting) {
163 for (int face = 0; face != 6; ++face) {
164 colors[face] = encode_light(light, f->light_source);
166 if (!f->light_source) {
167 applyFacesShading(colors[0], v3f(0, 1, 0));
168 applyFacesShading(colors[1], v3f(0, -1, 0));
169 applyFacesShading(colors[2], v3f(1, 0, 0));
170 applyFacesShading(colors[3], v3f(-1, 0, 0));
171 applyFacesShading(colors[4], v3f(0, 0, 1));
172 applyFacesShading(colors[5], v3f(0, 0, -1));
176 video::S3DVertex vertices[24] = {
178 video::S3DVertex(min.X, max.Y, max.Z, 0, 1, 0, colors[0], txc[0], txc[1]),
179 video::S3DVertex(max.X, max.Y, max.Z, 0, 1, 0, colors[0], txc[2], txc[1]),
180 video::S3DVertex(max.X, max.Y, min.Z, 0, 1, 0, colors[0], txc[2], txc[3]),
181 video::S3DVertex(min.X, max.Y, min.Z, 0, 1, 0, colors[0], txc[0], txc[3]),
183 video::S3DVertex(min.X, min.Y, min.Z, 0, -1, 0, colors[1], txc[4], txc[5]),
184 video::S3DVertex(max.X, min.Y, min.Z, 0, -1, 0, colors[1], txc[6], txc[5]),
185 video::S3DVertex(max.X, min.Y, max.Z, 0, -1, 0, colors[1], txc[6], txc[7]),
186 video::S3DVertex(min.X, min.Y, max.Z, 0, -1, 0, colors[1], txc[4], txc[7]),
188 video::S3DVertex(max.X, max.Y, min.Z, 1, 0, 0, colors[2], txc[ 8], txc[9]),
189 video::S3DVertex(max.X, max.Y, max.Z, 1, 0, 0, colors[2], txc[10], txc[9]),
190 video::S3DVertex(max.X, min.Y, max.Z, 1, 0, 0, colors[2], txc[10], txc[11]),
191 video::S3DVertex(max.X, min.Y, min.Z, 1, 0, 0, colors[2], txc[ 8], txc[11]),
193 video::S3DVertex(min.X, max.Y, max.Z, -1, 0, 0, colors[3], txc[12], txc[13]),
194 video::S3DVertex(min.X, max.Y, min.Z, -1, 0, 0, colors[3], txc[14], txc[13]),
195 video::S3DVertex(min.X, min.Y, min.Z, -1, 0, 0, colors[3], txc[14], txc[15]),
196 video::S3DVertex(min.X, min.Y, max.Z, -1, 0, 0, colors[3], txc[12], txc[15]),
198 video::S3DVertex(max.X, max.Y, max.Z, 0, 0, 1, colors[4], txc[16], txc[17]),
199 video::S3DVertex(min.X, max.Y, max.Z, 0, 0, 1, colors[4], txc[18], txc[17]),
200 video::S3DVertex(min.X, min.Y, max.Z, 0, 0, 1, colors[4], txc[18], txc[19]),
201 video::S3DVertex(max.X, min.Y, max.Z, 0, 0, 1, colors[4], txc[16], txc[19]),
203 video::S3DVertex(min.X, max.Y, min.Z, 0, 0, -1, colors[5], txc[20], txc[21]),
204 video::S3DVertex(max.X, max.Y, min.Z, 0, 0, -1, colors[5], txc[22], txc[21]),
205 video::S3DVertex(max.X, min.Y, min.Z, 0, 0, -1, colors[5], txc[22], txc[23]),
206 video::S3DVertex(min.X, min.Y, min.Z, 0, 0, -1, colors[5], txc[20], txc[23]),
209 static const u8 light_indices[24] = {
218 for (int face = 0; face < 6; face++) {
219 int tileindex = MYMIN(face, tilecount - 1);
220 const TileSpec &tile = tiles[tileindex];
221 for (int j = 0; j < 4; j++) {
222 video::S3DVertex &vertex = vertices[face * 4 + j];
223 v2f &tcoords = vertex.TCoords;
224 switch (tile.rotation) {
228 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
231 tcoords.rotateBy(180, irr::core::vector2df(0, 0));
234 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
237 tcoords.X = 1.0 - tcoords.X;
238 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
241 tcoords.X = 1.0 - tcoords.X;
242 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
245 tcoords.Y = 1.0 - tcoords.Y;
246 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
249 tcoords.Y = 1.0 - tcoords.Y;
250 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
253 tcoords.X = 1.0 - tcoords.X;
256 tcoords.Y = 1.0 - tcoords.Y;
264 if (data->m_smooth_lighting) {
265 for (int j = 0; j < 24; ++j) {
266 vertices[j].Color = encode_light(lights[light_indices[j]],
268 if (!f->light_source)
269 applyFacesShading(vertices[j].Color, vertices[j].Normal);
273 // Add to mesh collector
274 for (int k = 0; k < 6; ++k) {
275 int tileindex = MYMIN(k, tilecount - 1);
276 collector->append(tiles[tileindex], vertices + 4 * k, 4, quad_indices, 6);
280 // Gets the base lighting values for a node
281 void MapblockMeshGenerator::getSmoothLightFrame()
283 for (int k = 0; k < 8; ++k) {
284 LightPair light(getSmoothLightTransparent(blockpos_nodes + p, light_dirs[k], data));
285 frame.lightsA[k] = light.lightA;
286 frame.lightsB[k] = light.lightB;
290 // Calculates vertex light level
291 // vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
292 LightPair MapblockMeshGenerator::blendLight(const v3f &vertex_pos)
294 f32 x = core::clamp(vertex_pos.X / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
295 f32 y = core::clamp(vertex_pos.Y / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
296 f32 z = core::clamp(vertex_pos.Z / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
299 for (int k = 0; k < 8; ++k) {
300 f32 dx = (k & 4) ? x : 1 - x;
301 f32 dy = (k & 2) ? y : 1 - y;
302 f32 dz = (k & 1) ? z : 1 - z;
303 lightA += dx * dy * dz * frame.lightsA[k];
304 lightB += dx * dy * dz * frame.lightsB[k];
306 return LightPair(lightA, lightB);
309 // Calculates vertex color to be used in mapblock mesh
310 // vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
311 // tile_color - node's tile color
312 video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos)
314 LightPair light = blendLight(vertex_pos);
315 return encode_light(light, f->light_source);
318 video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos,
319 const v3f &vertex_normal)
321 video::SColor color = blendLightColor(vertex_pos);
322 if (!f->light_source)
323 applyFacesShading(color, vertex_normal);
327 void MapblockMeshGenerator::generateCuboidTextureCoords(const aabb3f &box, f32 *coords)
329 f32 tx1 = (box.MinEdge.X / BS) + 0.5;
330 f32 ty1 = (box.MinEdge.Y / BS) + 0.5;
331 f32 tz1 = (box.MinEdge.Z / BS) + 0.5;
332 f32 tx2 = (box.MaxEdge.X / BS) + 0.5;
333 f32 ty2 = (box.MaxEdge.Y / BS) + 0.5;
334 f32 tz2 = (box.MaxEdge.Z / BS) + 0.5;
336 tx1, 1 - tz2, tx2, 1 - tz1, // up
337 tx1, tz1, tx2, tz2, // down
338 tz1, 1 - ty2, tz2, 1 - ty1, // right
339 1 - tz2, 1 - ty2, 1 - tz1, 1 - ty1, // left
340 1 - tx2, 1 - ty2, 1 - tx1, 1 - ty1, // back
341 tx1, 1 - ty2, tx2, 1 - ty1, // front
343 for (int i = 0; i != 24; ++i)
347 void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc,
348 TileSpec *tiles, int tile_count)
350 f32 texture_coord_buf[24];
351 f32 dx1 = box.MinEdge.X;
352 f32 dy1 = box.MinEdge.Y;
353 f32 dz1 = box.MinEdge.Z;
354 f32 dx2 = box.MaxEdge.X;
355 f32 dy2 = box.MaxEdge.Y;
356 f32 dz2 = box.MaxEdge.Z;
357 box.MinEdge += origin;
358 box.MaxEdge += origin;
360 generateCuboidTextureCoords(box, texture_coord_buf);
361 txc = texture_coord_buf;
367 if (data->m_smooth_lighting) {
369 for (int j = 0; j < 8; ++j) {
371 d.X = (j & 4) ? dx2 : dx1;
372 d.Y = (j & 2) ? dy2 : dy1;
373 d.Z = (j & 1) ? dz2 : dz1;
374 lights[j] = blendLight(d);
376 drawCuboid(box, tiles, tile_count, lights, txc);
378 drawCuboid(box, tiles, tile_count, nullptr, txc);
382 void MapblockMeshGenerator::prepareLiquidNodeDrawing()
384 getSpecialTile(0, &tile_liquid_top);
385 getSpecialTile(1, &tile_liquid);
387 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y + 1, p.Z));
388 c_flowing = nodedef->getId(f->liquid_alternative_flowing);
389 c_source = nodedef->getId(f->liquid_alternative_source);
390 top_is_same_liquid = (ntop.getContent() == c_flowing) || (ntop.getContent() == c_source);
392 if (data->m_smooth_lighting)
393 return; // don't need to pre-compute anything in this case
395 if (f->light_source != 0) {
396 // If this liquid emits light and doesn't contain light, draw
397 // it at what it emits, for an increased effect
398 u8 e = decode_light(f->light_source);
399 light = LightPair(std::max(e, light.lightA), std::max(e, light.lightB));
400 } else if (nodedef->get(ntop).param_type == CPT_LIGHT) {
401 // Otherwise, use the light of the node on top if possible
402 light = LightPair(getInteriorLight(ntop, 0, nodedef));
405 color_liquid_top = encode_light(light, f->light_source);
406 color = encode_light(light, f->light_source);
409 void MapblockMeshGenerator::getLiquidNeighborhood()
411 u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8);
413 for (int w = -1; w <= 1; w++)
414 for (int u = -1; u <= 1; u++) {
415 NeighborData &neighbor = liquid_neighbors[w + 1][u + 1];
416 v3s16 p2 = p + v3s16(u, 0, w);
417 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
418 neighbor.content = n2.getContent();
419 neighbor.level = -0.5 * BS;
420 neighbor.is_same_liquid = false;
421 neighbor.top_is_same_liquid = false;
423 if (neighbor.content == CONTENT_IGNORE)
426 if (neighbor.content == c_source) {
427 neighbor.is_same_liquid = true;
428 neighbor.level = 0.5 * BS;
429 } else if (neighbor.content == c_flowing) {
430 neighbor.is_same_liquid = true;
431 u8 liquid_level = (n2.param2 & LIQUID_LEVEL_MASK);
432 if (liquid_level <= LIQUID_LEVEL_MAX + 1 - range)
435 liquid_level -= (LIQUID_LEVEL_MAX + 1 - range);
436 neighbor.level = (-0.5 + (liquid_level + 0.5) / range) * BS;
439 // Check node above neighbor.
440 // NOTE: This doesn't get executed if neighbor
443 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
444 if (n2.getContent() == c_source || n2.getContent() == c_flowing)
445 neighbor.top_is_same_liquid = true;
449 void MapblockMeshGenerator::calculateCornerLevels()
451 for (int k = 0; k < 2; k++)
452 for (int i = 0; i < 2; i++)
453 corner_levels[k][i] = getCornerLevel(i, k);
456 f32 MapblockMeshGenerator::getCornerLevel(int i, int k)
461 for (int dk = 0; dk < 2; dk++)
462 for (int di = 0; di < 2; di++) {
463 NeighborData &neighbor_data = liquid_neighbors[k + dk][i + di];
464 content_t content = neighbor_data.content;
466 // If top is liquid, draw starting from top of node
467 if (neighbor_data.top_is_same_liquid)
470 // Source always has the full height
471 if (content == c_source)
474 // Flowing liquid has level information
475 if (content == c_flowing) {
476 sum += neighbor_data.level;
478 } else if (content == CONTENT_AIR) {
481 return -0.5 * BS + 0.2;
489 void MapblockMeshGenerator::drawLiquidSides()
491 struct LiquidFaceDesc {
493 v3s16 p[2]; // XZ only; 1 means +, 0 means -
498 static const LiquidFaceDesc base_faces[4] = {
499 {v3s16( 1, 0, 0), {v3s16(1, 0, 1), v3s16(1, 0, 0)}},
500 {v3s16(-1, 0, 0), {v3s16(0, 0, 0), v3s16(0, 0, 1)}},
501 {v3s16( 0, 0, 1), {v3s16(0, 0, 1), v3s16(1, 0, 1)}},
502 {v3s16( 0, 0, -1), {v3s16(1, 0, 0), v3s16(0, 0, 0)}},
504 static const UV base_vertices[4] = {
511 for (const auto &face : base_faces) {
512 const NeighborData &neighbor = liquid_neighbors[face.dir.Z + 1][face.dir.X + 1];
514 // No face between nodes of the same liquid, unless there is node
515 // at the top to which it should be connected. Again, unless the face
516 // there would be inside the liquid
517 if (neighbor.is_same_liquid) {
518 if (!top_is_same_liquid)
520 if (neighbor.top_is_same_liquid)
524 const ContentFeatures &neighbor_features = nodedef->get(neighbor.content);
525 // Don't draw face if neighbor is blocking the view
526 if (neighbor_features.solidness == 2)
529 video::S3DVertex vertices[4];
530 for (int j = 0; j < 4; j++) {
531 const UV &vertex = base_vertices[j];
532 const v3s16 &base = face.p[vertex.u];
534 pos.X = (base.X - 0.5) * BS;
535 pos.Z = (base.Z - 0.5) * BS;
537 pos.Y = neighbor.is_same_liquid ? corner_levels[base.Z][base.X] : -0.5 * BS;
539 pos.Y = !top_is_same_liquid ? corner_levels[base.Z][base.X] : 0.5 * BS;
540 if (data->m_smooth_lighting)
541 color = blendLightColor(pos);
543 vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, color, vertex.u, vertex.v);
545 collector->append(tile_liquid, vertices, 4, quad_indices, 6);
549 void MapblockMeshGenerator::drawLiquidTop()
551 // To get backface culling right, the vertices need to go
552 // clockwise around the front of the face. And we happened to
553 // calculate corner levels in exact reverse order.
554 static const int corner_resolve[4][2] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
556 video::S3DVertex vertices[4] = {
557 video::S3DVertex(-BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 0, 1),
558 video::S3DVertex( BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 1, 1),
559 video::S3DVertex( BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 1, 0),
560 video::S3DVertex(-BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 0, 0),
563 for (int i = 0; i < 4; i++) {
564 int u = corner_resolve[i][0];
565 int w = corner_resolve[i][1];
566 vertices[i].Pos.Y += corner_levels[w][u];
567 if (data->m_smooth_lighting)
568 vertices[i].Color = blendLightColor(vertices[i].Pos);
569 vertices[i].Pos += origin;
572 // Default downwards-flowing texture animation goes from
573 // -Z towards +Z, thus the direction is +Z.
574 // Rotate texture to make animation go in flow direction
575 // Positive if liquid moves towards +Z
576 f32 dz = (corner_levels[0][0] + corner_levels[0][1]) -
577 (corner_levels[1][0] + corner_levels[1][1]);
578 // Positive if liquid moves towards +X
579 f32 dx = (corner_levels[0][0] + corner_levels[1][0]) -
580 (corner_levels[0][1] + corner_levels[1][1]);
581 f32 tcoord_angle = atan2(dz, dx) * core::RADTODEG;
582 v2f tcoord_center(0.5, 0.5);
583 v2f tcoord_translate(blockpos_nodes.Z + p.Z, blockpos_nodes.X + p.X);
584 tcoord_translate.rotateBy(tcoord_angle);
585 tcoord_translate.X -= floor(tcoord_translate.X);
586 tcoord_translate.Y -= floor(tcoord_translate.Y);
588 for (video::S3DVertex &vertex : vertices) {
589 vertex.TCoords.rotateBy(tcoord_angle, tcoord_center);
590 vertex.TCoords += tcoord_translate;
593 std::swap(vertices[0].TCoords, vertices[2].TCoords);
595 collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
598 void MapblockMeshGenerator::drawLiquidNode()
600 prepareLiquidNodeDrawing();
601 getLiquidNeighborhood();
602 calculateCornerLevels();
604 if (!top_is_same_liquid)
608 void MapblockMeshGenerator::drawGlasslikeNode()
612 for (int face = 0; face < 6; face++) {
613 // Check this neighbor
614 v3s16 dir = g_6dirs[face];
615 v3s16 neighbor_pos = blockpos_nodes + p + dir;
616 MapNode neighbor = data->m_vmanip.getNodeNoExNoEmerge(neighbor_pos);
617 // Don't make face if neighbor is of same type
618 if (neighbor.getContent() == n.getContent())
622 v3f(-BS / 2, BS / 2, -BS / 2),
623 v3f( BS / 2, BS / 2, -BS / 2),
624 v3f( BS / 2, -BS / 2, -BS / 2),
625 v3f(-BS / 2, -BS / 2, -BS / 2),
628 for (v3f &vertex : vertices) {
631 vertex.rotateXZBy(180); break;
633 vertex.rotateYZBy( 90); break;
635 vertex.rotateXZBy( 90); break;
637 vertex.rotateXZBy( 0); break;
639 vertex.rotateYZBy(-90); break;
641 vertex.rotateXZBy(-90); break;
644 drawQuad(vertices, dir);
648 void MapblockMeshGenerator::drawGlasslikeFramedNode()
651 for (int face = 0; face < 6; face++)
652 getTile(g_6dirs[face], &tiles[face]);
654 TileSpec glass_tiles[6];
655 if (tiles[1].layers[0].texture &&
656 tiles[2].layers[0].texture &&
657 tiles[3].layers[0].texture) {
658 glass_tiles[0] = tiles[4];
659 glass_tiles[1] = tiles[0];
660 glass_tiles[2] = tiles[4];
661 glass_tiles[3] = tiles[4];
662 glass_tiles[4] = tiles[3];
663 glass_tiles[5] = tiles[4];
665 for (auto &glass_tile : glass_tiles)
666 glass_tile = tiles[4];
669 u8 param2 = n.getParam2();
670 bool H_merge = !(param2 & 128);
671 bool V_merge = !(param2 & 64);
674 static const float a = BS / 2;
675 static const float g = a - 0.003;
676 static const float b = .876 * ( BS / 2 );
678 static const aabb3f frame_edges[FRAMED_EDGE_COUNT] = {
679 aabb3f( b, b, -a, a, a, a), // y+
680 aabb3f(-a, b, -a, -b, a, a), // y+
681 aabb3f( b, -a, -a, a, -b, a), // y-
682 aabb3f(-a, -a, -a, -b, -b, a), // y-
683 aabb3f( b, -a, b, a, a, a), // x+
684 aabb3f( b, -a, -a, a, a, -b), // x+
685 aabb3f(-a, -a, b, -b, a, a), // x-
686 aabb3f(-a, -a, -a, -b, a, -b), // x-
687 aabb3f(-a, b, b, a, a, a), // z+
688 aabb3f(-a, -a, b, a, -b, a), // z+
689 aabb3f(-a, -a, -a, a, -b, -b), // z-
690 aabb3f(-a, b, -a, a, a, -b), // z-
692 static const aabb3f glass_faces[6] = {
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+
696 aabb3f(-g, -g, -g, g, g, -g), // z-
697 aabb3f(-g, -g, -g, g, -g, g), // y-
698 aabb3f(-g, -g, -g, -g, g, g), // x-
701 // tables of neighbour (connect if same type and merge allowed),
702 // checked with g_26dirs
704 // 1 = connect, 0 = face visible
705 bool nb[FRAMED_NEIGHBOR_COUNT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
708 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};
709 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};
710 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};
711 const bool *check_nb = check_nb_all;
713 // neighbours checks for frames visibility
714 if (H_merge || V_merge) {
716 check_nb = check_nb_vertical; // vertical-only merge
718 check_nb = check_nb_horizontal; // horizontal-only merge
719 content_t current = n.getContent();
720 for (int i = 0; i < FRAMED_NEIGHBOR_COUNT; i++) {
723 v3s16 n2p = blockpos_nodes + p + g_26dirs[i];
724 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
725 content_t n2c = n2.getContent();
733 static const u8 nb_triplet[FRAMED_EDGE_COUNT][3] = {
734 {1, 2, 7}, {1, 5, 6}, {4, 2, 15}, {4, 5, 14},
735 {2, 0, 11}, {2, 3, 13}, {5, 0, 10}, {5, 3, 12},
736 {0, 1, 8}, {0, 4, 16}, {3, 4, 17}, {3, 1, 9},
740 for (int edge = 0; edge < FRAMED_EDGE_COUNT; edge++) {
742 if (nb[nb_triplet[edge][2]])
743 edge_invisible = nb[nb_triplet[edge][0]] & nb[nb_triplet[edge][1]];
745 edge_invisible = nb[nb_triplet[edge][0]] ^ nb[nb_triplet[edge][1]];
748 drawAutoLightedCuboid(frame_edges[edge]);
751 for (int face = 0; face < 6; face++) {
754 tile = glass_tiles[face];
755 drawAutoLightedCuboid(glass_faces[face]);
758 // Optionally render internal liquid level defined by param2
759 // Liquid is textured with 1 tile defined in nodedef 'special_tiles'
760 if (param2 > 0 && f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL &&
761 f->special_tiles[0].layers[0].texture) {
762 // Internal liquid level has param2 range 0 .. 63,
763 // convert it to -0.5 .. 0.5
764 float vlev = (param2 / 63.0) * 2.0 - 1.0;
765 getSpecialTile(0, &tile);
766 drawAutoLightedCuboid(aabb3f(-(nb[5] ? g : b),
770 (nb[1] ? g : b) * vlev,
775 void MapblockMeshGenerator::drawAllfacesNode()
777 static const aabb3f box(-BS / 2, -BS / 2, -BS / 2, BS / 2, BS / 2, BS / 2);
779 drawAutoLightedCuboid(box);
782 void MapblockMeshGenerator::drawTorchlikeNode()
784 u8 wall = n.getWallMounted(nodedef);
787 case DWM_YP: tileindex = 1; break; // ceiling
788 case DWM_YN: tileindex = 0; break; // floor
789 default: tileindex = 2; // side (or invalid—should we care?)
791 useTile(tileindex, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
793 float size = BS / 2 * f->visual_scale;
797 v3f( size, -size, 0),
798 v3f(-size, -size, 0),
801 for (v3f &vertex : vertices) {
804 vertex.rotateXZBy(-45); break;
806 vertex.rotateXZBy( 45); break;
808 vertex.rotateXZBy( 0); break;
810 vertex.rotateXZBy(180); break;
812 vertex.rotateXZBy( 90); break;
814 vertex.rotateXZBy(-90); break;
820 void MapblockMeshGenerator::drawSignlikeNode()
822 u8 wall = n.getWallMounted(nodedef);
823 useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
824 static const float offset = BS / 16;
825 float size = BS / 2 * f->visual_scale;
826 // Wall at X+ of node
828 v3f(BS / 2 - offset, size, size),
829 v3f(BS / 2 - offset, size, -size),
830 v3f(BS / 2 - offset, -size, -size),
831 v3f(BS / 2 - offset, -size, size),
834 for (v3f &vertex : vertices) {
837 vertex.rotateXYBy( 90); break;
839 vertex.rotateXYBy(-90); break;
841 vertex.rotateXZBy( 0); break;
843 vertex.rotateXZBy(180); break;
845 vertex.rotateXZBy( 90); break;
847 vertex.rotateXZBy(-90); break;
853 void MapblockMeshGenerator::drawPlantlikeQuad(float rotation, float quad_offset,
854 bool offset_top_only)
857 v3f(-scale, -BS / 2 + 2.0 * scale * plant_height, 0),
858 v3f( scale, -BS / 2 + 2.0 * scale * plant_height, 0),
859 v3f( scale, -BS / 2, 0),
860 v3f(-scale, -BS / 2, 0),
862 if (random_offset_Y) {
863 PseudoRandom yrng(face_num++ | p.X << 16 | p.Z << 8 | p.Y << 24);
864 offset.Y = -BS * ((yrng.next() % 16 / 16.0) * 0.125);
866 int offset_count = offset_top_only ? 2 : 4;
867 for (int i = 0; i < offset_count; i++)
868 vertices[i].Z += quad_offset;
870 for (v3f &vertex : vertices) {
871 vertex.rotateXZBy(rotation + rotate_degree);
874 drawQuad(vertices, v3s16(0, 0, 0), plant_height);
877 void MapblockMeshGenerator::drawPlantlike()
879 draw_style = PLANT_STYLE_CROSS;
880 scale = BS / 2 * f->visual_scale;
881 offset = v3f(0, 0, 0);
883 random_offset_Y = false;
887 switch (f->param_type_2) {
888 case CPT2_MESHOPTIONS:
889 draw_style = PlantlikeStyle(n.param2 & MO_MASK_STYLE);
890 if (n.param2 & MO_BIT_SCALE_SQRT2)
892 if (n.param2 & MO_BIT_RANDOM_OFFSET) {
893 PseudoRandom rng(p.X << 8 | p.Z | p.Y << 16);
894 offset.X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
895 offset.Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
897 if (n.param2 & MO_BIT_RANDOM_OFFSET_Y)
898 random_offset_Y = true;
902 rotate_degree = n.param2 * 2;
906 plant_height = n.param2 / 16.0;
913 switch (draw_style) {
914 case PLANT_STYLE_CROSS:
915 drawPlantlikeQuad(46);
916 drawPlantlikeQuad(-44);
919 case PLANT_STYLE_CROSS2:
920 drawPlantlikeQuad(91);
921 drawPlantlikeQuad(1);
924 case PLANT_STYLE_STAR:
925 drawPlantlikeQuad(121);
926 drawPlantlikeQuad(241);
927 drawPlantlikeQuad(1);
930 case PLANT_STYLE_HASH:
931 drawPlantlikeQuad( 1, BS / 4);
932 drawPlantlikeQuad( 91, BS / 4);
933 drawPlantlikeQuad(181, BS / 4);
934 drawPlantlikeQuad(271, BS / 4);
937 case PLANT_STYLE_HASH2:
938 drawPlantlikeQuad( 1, -BS / 2, true);
939 drawPlantlikeQuad( 91, -BS / 2, true);
940 drawPlantlikeQuad(181, -BS / 2, true);
941 drawPlantlikeQuad(271, -BS / 2, true);
946 void MapblockMeshGenerator::drawPlantlikeNode()
952 void MapblockMeshGenerator::drawPlantlikeRootedNode()
954 useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, 0, true);
955 origin += v3f(0.0, BS, 0.0);
957 if (data->m_smooth_lighting) {
958 getSmoothLightFrame();
960 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
961 light = LightPair(getInteriorLight(ntop, 1, nodedef));
967 void MapblockMeshGenerator::drawFirelikeQuad(float rotation, float opening_angle,
968 float offset_h, float offset_v)
971 v3f(-scale, -BS / 2 + scale * 2, 0),
972 v3f( scale, -BS / 2 + scale * 2, 0),
973 v3f( scale, -BS / 2, 0),
974 v3f(-scale, -BS / 2, 0),
977 for (v3f &vertex : vertices) {
978 vertex.rotateYZBy(opening_angle);
979 vertex.Z += offset_h;
980 vertex.rotateXZBy(rotation);
981 vertex.Y += offset_v;
986 void MapblockMeshGenerator::drawFirelikeNode()
989 scale = BS / 2 * f->visual_scale;
991 // Check for adjacent nodes
992 bool neighbors = false;
993 bool neighbor[6] = {0, 0, 0, 0, 0, 0};
994 content_t current = n.getContent();
995 for (int i = 0; i < 6; i++) {
996 v3s16 n2p = blockpos_nodes + p + g_6dirs[i];
997 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
998 content_t n2c = n2.getContent();
999 if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
1004 bool drawBasicFire = neighbor[D6D_YN] || !neighbors;
1005 bool drawBottomFire = neighbor[D6D_YP];
1007 if (drawBasicFire || neighbor[D6D_ZP])
1008 drawFirelikeQuad(0, -10, 0.4 * BS);
1009 else if (drawBottomFire)
1010 drawFirelikeQuad(0, 70, 0.47 * BS, 0.484 * BS);
1012 if (drawBasicFire || neighbor[D6D_XN])
1013 drawFirelikeQuad(90, -10, 0.4 * BS);
1014 else if (drawBottomFire)
1015 drawFirelikeQuad(90, 70, 0.47 * BS, 0.484 * BS);
1017 if (drawBasicFire || neighbor[D6D_ZN])
1018 drawFirelikeQuad(180, -10, 0.4 * BS);
1019 else if (drawBottomFire)
1020 drawFirelikeQuad(180, 70, 0.47 * BS, 0.484 * BS);
1022 if (drawBasicFire || neighbor[D6D_XP])
1023 drawFirelikeQuad(270, -10, 0.4 * BS);
1024 else if (drawBottomFire)
1025 drawFirelikeQuad(270, 70, 0.47 * BS, 0.484 * BS);
1027 if (drawBasicFire) {
1028 drawFirelikeQuad(45, 0, 0.0);
1029 drawFirelikeQuad(-45, 0, 0.0);
1033 void MapblockMeshGenerator::drawFencelikeNode()
1036 TileSpec tile_nocrack = tile;
1038 for (auto &layer : tile_nocrack.layers)
1039 layer.material_flags &= ~MATERIAL_FLAG_CRACK;
1041 // Put wood the right way around in the posts
1042 TileSpec tile_rot = tile;
1043 tile_rot.rotation = 1;
1045 static const f32 post_rad = BS / 8;
1046 static const f32 bar_rad = BS / 16;
1047 static const f32 bar_len = BS / 2 - post_rad;
1049 // The post - always present
1050 static const aabb3f post(-post_rad, -BS / 2, -post_rad,
1051 post_rad, BS / 2, post_rad);
1052 static const f32 postuv[24] = {
1053 0.375, 0.375, 0.625, 0.625,
1054 0.375, 0.375, 0.625, 0.625,
1055 0.000, 0.000, 0.250, 1.000,
1056 0.250, 0.000, 0.500, 1.000,
1057 0.500, 0.000, 0.750, 1.000,
1058 0.750, 0.000, 1.000, 1.000,
1061 drawAutoLightedCuboid(post, postuv);
1063 tile = tile_nocrack;
1065 // Now a section of fence, +X, if there's a post there
1068 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1069 const ContentFeatures *f2 = &nodedef->get(n2);
1070 if (f2->drawtype == NDT_FENCELIKE) {
1071 static const aabb3f bar_x1(BS / 2 - bar_len, BS / 4 - bar_rad, -bar_rad,
1072 BS / 2 + bar_len, BS / 4 + bar_rad, bar_rad);
1073 static const aabb3f bar_x2(BS / 2 - bar_len, -BS / 4 - bar_rad, -bar_rad,
1074 BS / 2 + bar_len, -BS / 4 + bar_rad, bar_rad);
1075 static const f32 xrailuv[24] = {
1076 0.000, 0.125, 1.000, 0.250,
1077 0.000, 0.250, 1.000, 0.375,
1078 0.375, 0.375, 0.500, 0.500,
1079 0.625, 0.625, 0.750, 0.750,
1080 0.000, 0.500, 1.000, 0.625,
1081 0.000, 0.875, 1.000, 1.000,
1083 drawAutoLightedCuboid(bar_x1, xrailuv);
1084 drawAutoLightedCuboid(bar_x2, xrailuv);
1087 // Now a section of fence, +Z, if there's a post there
1090 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1091 f2 = &nodedef->get(n2);
1092 if (f2->drawtype == NDT_FENCELIKE) {
1093 static const aabb3f bar_z1(-bar_rad, BS / 4 - bar_rad, BS / 2 - bar_len,
1094 bar_rad, BS / 4 + bar_rad, BS / 2 + bar_len);
1095 static const aabb3f bar_z2(-bar_rad, -BS / 4 - bar_rad, BS / 2 - bar_len,
1096 bar_rad, -BS / 4 + bar_rad, BS / 2 + bar_len);
1097 static const f32 zrailuv[24] = {
1098 0.1875, 0.0625, 0.3125, 0.3125, // cannot rotate; stretch
1099 0.2500, 0.0625, 0.3750, 0.3125, // for wood texture instead
1100 0.0000, 0.5625, 1.0000, 0.6875,
1101 0.0000, 0.3750, 1.0000, 0.5000,
1102 0.3750, 0.3750, 0.5000, 0.5000,
1103 0.6250, 0.6250, 0.7500, 0.7500,
1105 drawAutoLightedCuboid(bar_z1, zrailuv);
1106 drawAutoLightedCuboid(bar_z2, zrailuv);
1110 bool MapblockMeshGenerator::isSameRail(v3s16 dir)
1112 MapNode node2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
1113 if (node2.getContent() == n.getContent())
1115 const ContentFeatures &def2 = nodedef->get(node2);
1116 return ((def2.drawtype == NDT_RAILLIKE) &&
1117 (def2.getGroup(raillike_groupname) == raillike_group));
1120 void MapblockMeshGenerator::drawRaillikeNode()
1122 static const v3s16 direction[4] = {
1128 static const int slope_angle[4] = {0, 180, 90, -90};
1140 static const RailDesc rail_kinds[16] = {
1143 {straight, 0}, // . . . .
1144 {straight, 0}, // . . . +Z
1145 {straight, 0}, // . . -Z .
1146 {straight, 0}, // . . -Z +Z
1147 {straight, 90}, // . -X . .
1148 { curved, 180}, // . -X . +Z
1149 { curved, 270}, // . -X -Z .
1150 {junction, 180}, // . -X -Z +Z
1151 {straight, 90}, // +X . . .
1152 { curved, 90}, // +X . . +Z
1153 { curved, 0}, // +X . -Z .
1154 {junction, 0}, // +X . -Z +Z
1155 {straight, 90}, // +X -X . .
1156 {junction, 90}, // +X -X . +Z
1157 {junction, 270}, // +X -X -Z .
1158 { cross, 0}, // +X -X -Z +Z
1161 raillike_group = nodedef->get(n).getGroup(raillike_groupname);
1166 bool sloped = false;
1167 for (int dir = 0; dir < 4; dir++) {
1168 bool rail_above = isSameRail(direction[dir] + v3s16(0, 1, 0));
1171 angle = slope_angle[dir];
1174 isSameRail(direction[dir]) ||
1175 isSameRail(direction[dir] + v3s16(0, -1, 0)))
1180 tile_index = straight;
1182 tile_index = rail_kinds[code].tile_index;
1183 angle = rail_kinds[code].angle;
1186 useTile(tile_index, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
1188 static const float offset = BS / 64;
1189 static const float size = BS / 2;
1190 float y2 = sloped ? size : -size;
1192 v3f(-size, y2 + offset, size),
1193 v3f( size, y2 + offset, size),
1194 v3f( size, -size + offset, -size),
1195 v3f(-size, -size + offset, -size),
1198 for (v3f &vertex : vertices)
1199 vertex.rotateXZBy(angle);
1203 void MapblockMeshGenerator::drawNodeboxNode()
1205 static const v3s16 tile_dirs[6] = {
1214 // we have this order for some reason...
1215 static const v3s16 connection_dirs[6] = {
1216 v3s16( 0, 1, 0), // top
1217 v3s16( 0, -1, 0), // bottom
1218 v3s16( 0, 0, -1), // front
1219 v3s16(-1, 0, 0), // left
1220 v3s16( 0, 0, 1), // back
1221 v3s16( 1, 0, 0), // right
1225 for (int face = 0; face < 6; face++) {
1226 // Handles facedir rotation for textures
1227 getTile(tile_dirs[face], &tiles[face]);
1230 // locate possible neighboring nodes to connect to
1231 int neighbors_set = 0;
1232 if (f->node_box.type == NODEBOX_CONNECTED) {
1233 for (int dir = 0; dir != 6; dir++) {
1234 int flag = 1 << dir;
1235 v3s16 p2 = blockpos_nodes + p + connection_dirs[dir];
1236 MapNode n2 = data->m_vmanip.getNodeNoEx(p2);
1237 if (nodedef->nodeboxConnects(n, n2, flag))
1238 neighbors_set |= flag;
1242 std::vector<aabb3f> boxes;
1243 n.getNodeBoxes(nodedef, &boxes, neighbors_set);
1244 for (const auto &box : boxes)
1245 drawAutoLightedCuboid(box, nullptr, tiles, 6);
1248 void MapblockMeshGenerator::drawMeshNode()
1252 bool private_mesh; // as a grab/drop pair is not thread-safe
1254 if (f->param_type_2 == CPT2_FACEDIR ||
1255 f->param_type_2 == CPT2_COLORED_FACEDIR) {
1256 facedir = n.getFaceDir(nodedef);
1257 } else if (f->param_type_2 == CPT2_WALLMOUNTED ||
1258 f->param_type_2 == CPT2_COLORED_WALLMOUNTED) {
1259 // Convert wallmounted to 6dfacedir.
1260 // When cache enabled, it is already converted.
1261 facedir = n.getWallMounted(nodedef);
1262 if (!enable_mesh_cache)
1263 facedir = wallmounted_to_facedir[facedir];
1266 if (!data->m_smooth_lighting && f->mesh_ptr[facedir]) {
1267 // use cached meshes
1268 private_mesh = false;
1269 mesh = f->mesh_ptr[facedir];
1270 } else if (f->mesh_ptr[0]) {
1271 // no cache, clone and rotate mesh
1272 private_mesh = true;
1273 mesh = cloneMesh(f->mesh_ptr[0]);
1274 rotateMeshBy6dFacedir(mesh, facedir);
1275 recalculateBoundingBox(mesh);
1276 meshmanip->recalculateNormals(mesh, true, false);
1280 int mesh_buffer_count = mesh->getMeshBufferCount();
1281 for (int j = 0; j < mesh_buffer_count; j++) {
1283 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1284 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1285 int vertex_count = buf->getVertexCount();
1287 if (data->m_smooth_lighting) {
1288 // Mesh is always private here. So the lighting is applied to each
1289 // vertex right here.
1290 for (int k = 0; k < vertex_count; k++) {
1291 video::S3DVertex &vertex = vertices[k];
1292 vertex.Color = blendLightColor(vertex.Pos, vertex.Normal);
1293 vertex.Pos += origin;
1295 collector->append(tile, vertices, vertex_count,
1296 buf->getIndices(), buf->getIndexCount());
1298 // Don't modify the mesh, it may not be private here.
1299 // Instead, let the collector process colors, etc.
1300 collector->append(tile, vertices, vertex_count,
1301 buf->getIndices(), buf->getIndexCount(), origin,
1302 color, f->light_source);
1309 // also called when the drawtype is known but should have been pre-converted
1310 void MapblockMeshGenerator::errorUnknownDrawtype()
1312 infostream << "Got drawtype " << f->drawtype << std::endl;
1313 FATAL_ERROR("Unknown drawtype");
1316 void MapblockMeshGenerator::drawNode()
1318 // skip some drawtypes early
1319 switch (f->drawtype) {
1320 case NDT_NORMAL: // Drawn by MapBlockMesh
1321 case NDT_AIRLIKE: // Not drawn at all
1322 case NDT_LIQUID: // Drawn by MapBlockMesh
1327 origin = intToFloat(p, BS);
1328 if (data->m_smooth_lighting)
1329 getSmoothLightFrame();
1331 light = LightPair(getInteriorLight(n, 1, nodedef));
1332 switch (f->drawtype) {
1333 case NDT_FLOWINGLIQUID: drawLiquidNode(); break;
1334 case NDT_GLASSLIKE: drawGlasslikeNode(); break;
1335 case NDT_GLASSLIKE_FRAMED: drawGlasslikeFramedNode(); break;
1336 case NDT_ALLFACES: drawAllfacesNode(); break;
1337 case NDT_TORCHLIKE: drawTorchlikeNode(); break;
1338 case NDT_SIGNLIKE: drawSignlikeNode(); break;
1339 case NDT_PLANTLIKE: drawPlantlikeNode(); break;
1340 case NDT_PLANTLIKE_ROOTED: drawPlantlikeRootedNode(); break;
1341 case NDT_FIRELIKE: drawFirelikeNode(); break;
1342 case NDT_FENCELIKE: drawFencelikeNode(); break;
1343 case NDT_RAILLIKE: drawRaillikeNode(); break;
1344 case NDT_NODEBOX: drawNodeboxNode(); break;
1345 case NDT_MESH: drawMeshNode(); break;
1346 default: errorUnknownDrawtype(); break;
1351 TODO: Fix alpha blending for special nodes
1352 Currently only the last element rendered is blended correct
1354 void MapblockMeshGenerator::generate()
1356 for (p.Z = 0; p.Z < MAP_BLOCKSIZE; p.Z++)
1357 for (p.Y = 0; p.Y < MAP_BLOCKSIZE; p.Y++)
1358 for (p.X = 0; p.X < MAP_BLOCKSIZE; p.X++) {
1359 n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1360 f = &nodedef->get(n);
1365 void MapblockMeshGenerator::renderSingle(content_t node)
1368 n = MapNode(node, 0xff, 0x00);
1369 f = &nodedef->get(n);