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 = NULL;
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 u16 *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 u16 light = getSmoothLightTransparent(blockpos_nodes + p, light_dirs[k], data);
285 frame.lightsA[k] = light & 0xff;
286 frame.lightsB[k] = light >> 8;
290 // Calculates vertex light level
291 // vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
292 u16 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];
307 core::clamp(core::round32(lightA), 0, 255) |
308 core::clamp(core::round32(lightB), 0, 255) << 8;
311 // Calculates vertex color to be used in mapblock mesh
312 // vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
313 // tile_color - node's tile color
314 video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos)
316 u16 light = blendLight(vertex_pos);
317 return encode_light(light, f->light_source);
320 video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos,
321 const v3f &vertex_normal)
323 video::SColor color = blendLightColor(vertex_pos);
324 if (!f->light_source)
325 applyFacesShading(color, vertex_normal);
329 void MapblockMeshGenerator::generateCuboidTextureCoords(const aabb3f &box, f32 *coords)
331 f32 tx1 = (box.MinEdge.X / BS) + 0.5;
332 f32 ty1 = (box.MinEdge.Y / BS) + 0.5;
333 f32 tz1 = (box.MinEdge.Z / BS) + 0.5;
334 f32 tx2 = (box.MaxEdge.X / BS) + 0.5;
335 f32 ty2 = (box.MaxEdge.Y / BS) + 0.5;
336 f32 tz2 = (box.MaxEdge.Z / BS) + 0.5;
338 tx1, 1 - tz2, tx2, 1 - tz1, // up
339 tx1, tz1, tx2, tz2, // down
340 tz1, 1 - ty2, tz2, 1 - ty1, // right
341 1 - tz2, 1 - ty2, 1 - tz1, 1 - ty1, // left
342 1 - tx2, 1 - ty2, 1 - tx1, 1 - ty1, // back
343 tx1, 1 - ty2, tx2, 1 - ty1, // front
345 for (int i = 0; i != 24; ++i)
349 void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc,
350 TileSpec *tiles, int tile_count)
352 f32 texture_coord_buf[24];
353 f32 dx1 = box.MinEdge.X;
354 f32 dy1 = box.MinEdge.Y;
355 f32 dz1 = box.MinEdge.Z;
356 f32 dx2 = box.MaxEdge.X;
357 f32 dy2 = box.MaxEdge.Y;
358 f32 dz2 = box.MaxEdge.Z;
359 box.MinEdge += origin;
360 box.MaxEdge += origin;
362 generateCuboidTextureCoords(box, texture_coord_buf);
363 txc = texture_coord_buf;
369 if (data->m_smooth_lighting) {
371 for (int j = 0; j < 8; ++j) {
373 d.X = (j & 4) ? dx2 : dx1;
374 d.Y = (j & 2) ? dy2 : dy1;
375 d.Z = (j & 1) ? dz2 : dz1;
376 lights[j] = blendLight(d);
378 drawCuboid(box, tiles, tile_count, lights, txc);
380 drawCuboid(box, tiles, tile_count, NULL, txc);
384 void MapblockMeshGenerator::prepareLiquidNodeDrawing()
386 getSpecialTile(0, &tile_liquid_top);
387 getSpecialTile(1, &tile_liquid);
389 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y + 1, p.Z));
390 c_flowing = nodedef->getId(f->liquid_alternative_flowing);
391 c_source = nodedef->getId(f->liquid_alternative_source);
392 top_is_same_liquid = (ntop.getContent() == c_flowing) || (ntop.getContent() == c_source);
394 if (data->m_smooth_lighting)
395 return; // don't need to pre-compute anything in this case
397 if (f->light_source != 0) {
398 // If this liquid emits light and doesn't contain light, draw
399 // it at what it emits, for an increased effect
400 light = decode_light(f->light_source);
401 light = light | (light << 8);
402 } else if (nodedef->get(ntop).param_type == CPT_LIGHT) {
403 // Otherwise, use the light of the node on top if possible
404 light = getInteriorLight(ntop, 0, nodedef);
407 color_liquid_top = encode_light(light, f->light_source);
408 color = encode_light(light, f->light_source);
411 void MapblockMeshGenerator::getLiquidNeighborhood()
413 u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8);
415 for (int w = -1; w <= 1; w++)
416 for (int u = -1; u <= 1; u++) {
417 NeighborData &neighbor = liquid_neighbors[w + 1][u + 1];
418 v3s16 p2 = p + v3s16(u, 0, w);
419 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
420 neighbor.content = n2.getContent();
421 neighbor.level = -0.5 * BS;
422 neighbor.is_same_liquid = false;
423 neighbor.top_is_same_liquid = false;
425 if (neighbor.content == CONTENT_IGNORE)
428 if (neighbor.content == c_source) {
429 neighbor.is_same_liquid = true;
430 neighbor.level = 0.5 * BS;
431 } else if (neighbor.content == c_flowing) {
432 neighbor.is_same_liquid = true;
433 u8 liquid_level = (n2.param2 & LIQUID_LEVEL_MASK);
434 if (liquid_level <= LIQUID_LEVEL_MAX + 1 - range)
437 liquid_level -= (LIQUID_LEVEL_MAX + 1 - range);
438 neighbor.level = (-0.5 + (liquid_level + 0.5) / range) * BS;
441 // Check node above neighbor.
442 // NOTE: This doesn't get executed if neighbor
445 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
446 if (n2.getContent() == c_source || n2.getContent() == c_flowing)
447 neighbor.top_is_same_liquid = true;
451 void MapblockMeshGenerator::calculateCornerLevels()
453 for (int k = 0; k < 2; k++)
454 for (int i = 0; i < 2; i++)
455 corner_levels[k][i] = getCornerLevel(i, k);
458 f32 MapblockMeshGenerator::getCornerLevel(int i, int k)
463 for (int dk = 0; dk < 2; dk++)
464 for (int di = 0; di < 2; di++) {
465 NeighborData &neighbor_data = liquid_neighbors[k + dk][i + di];
466 content_t content = neighbor_data.content;
468 // If top is liquid, draw starting from top of node
469 if (neighbor_data.top_is_same_liquid)
472 // Source always has the full height
473 if (content == c_source)
476 // Flowing liquid has level information
477 if (content == c_flowing) {
478 sum += neighbor_data.level;
480 } else if (content == CONTENT_AIR) {
483 return -0.5 * BS + 0.2;
491 void MapblockMeshGenerator::drawLiquidSides()
493 struct LiquidFaceDesc {
495 v3s16 p[2]; // XZ only; 1 means +, 0 means -
500 static const LiquidFaceDesc base_faces[4] = {
501 {v3s16( 1, 0, 0), {v3s16(1, 0, 1), v3s16(1, 0, 0)}},
502 {v3s16(-1, 0, 0), {v3s16(0, 0, 0), v3s16(0, 0, 1)}},
503 {v3s16( 0, 0, 1), {v3s16(0, 0, 1), v3s16(1, 0, 1)}},
504 {v3s16( 0, 0, -1), {v3s16(1, 0, 0), v3s16(0, 0, 0)}},
506 static const UV base_vertices[4] = {
513 for (const auto &face : base_faces) {
514 const NeighborData &neighbor = liquid_neighbors[face.dir.Z + 1][face.dir.X + 1];
516 // No face between nodes of the same liquid, unless there is node
517 // at the top to which it should be connected. Again, unless the face
518 // there would be inside the liquid
519 if (neighbor.is_same_liquid) {
520 if (!top_is_same_liquid)
522 if (neighbor.top_is_same_liquid)
526 const ContentFeatures &neighbor_features = nodedef->get(neighbor.content);
527 // Don't draw face if neighbor is blocking the view
528 if (neighbor_features.solidness == 2)
531 video::S3DVertex vertices[4];
532 for (int j = 0; j < 4; j++) {
533 const UV &vertex = base_vertices[j];
534 const v3s16 &base = face.p[vertex.u];
536 pos.X = (base.X - 0.5) * BS;
537 pos.Z = (base.Z - 0.5) * BS;
539 pos.Y = neighbor.is_same_liquid ? corner_levels[base.Z][base.X] : -0.5 * BS;
541 pos.Y = !top_is_same_liquid ? corner_levels[base.Z][base.X] : 0.5 * BS;
542 if (data->m_smooth_lighting)
543 color = blendLightColor(pos);
545 vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, color, vertex.u, vertex.v);
547 collector->append(tile_liquid, vertices, 4, quad_indices, 6);
551 void MapblockMeshGenerator::drawLiquidTop()
553 // To get backface culling right, the vertices need to go
554 // clockwise around the front of the face. And we happened to
555 // calculate corner levels in exact reverse order.
556 static const int corner_resolve[4][2] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
558 video::S3DVertex vertices[4] = {
559 video::S3DVertex(-BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 0, 1),
560 video::S3DVertex( BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 1, 1),
561 video::S3DVertex( BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 1, 0),
562 video::S3DVertex(-BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 0, 0),
565 for (int i = 0; i < 4; i++) {
566 int u = corner_resolve[i][0];
567 int w = corner_resolve[i][1];
568 vertices[i].Pos.Y += corner_levels[w][u];
569 if (data->m_smooth_lighting)
570 vertices[i].Color = blendLightColor(vertices[i].Pos);
571 vertices[i].Pos += origin;
574 // Default downwards-flowing texture animation goes from
575 // -Z towards +Z, thus the direction is +Z.
576 // Rotate texture to make animation go in flow direction
577 // Positive if liquid moves towards +Z
578 f32 dz = (corner_levels[0][0] + corner_levels[0][1]) -
579 (corner_levels[1][0] + corner_levels[1][1]);
580 // Positive if liquid moves towards +X
581 f32 dx = (corner_levels[0][0] + corner_levels[1][0]) -
582 (corner_levels[0][1] + corner_levels[1][1]);
583 f32 tcoord_angle = atan2(dz, dx) * core::RADTODEG;
584 v2f tcoord_center(0.5, 0.5);
585 v2f tcoord_translate(blockpos_nodes.Z + p.Z, blockpos_nodes.X + p.X);
586 tcoord_translate.rotateBy(tcoord_angle);
587 tcoord_translate.X -= floor(tcoord_translate.X);
588 tcoord_translate.Y -= floor(tcoord_translate.Y);
590 for (video::S3DVertex &vertex : vertices) {
591 vertex.TCoords.rotateBy(tcoord_angle, tcoord_center);
592 vertex.TCoords += tcoord_translate;
595 std::swap(vertices[0].TCoords, vertices[2].TCoords);
597 collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
600 void MapblockMeshGenerator::drawLiquidNode()
602 prepareLiquidNodeDrawing();
603 getLiquidNeighborhood();
604 calculateCornerLevels();
606 if (!top_is_same_liquid)
610 void MapblockMeshGenerator::drawGlasslikeNode()
614 for (int face = 0; face < 6; face++) {
615 // Check this neighbor
616 v3s16 dir = g_6dirs[face];
617 v3s16 neighbor_pos = blockpos_nodes + p + dir;
618 MapNode neighbor = data->m_vmanip.getNodeNoExNoEmerge(neighbor_pos);
619 // Don't make face if neighbor is of same type
620 if (neighbor.getContent() == n.getContent())
624 v3f(-BS / 2, BS / 2, -BS / 2),
625 v3f( BS / 2, BS / 2, -BS / 2),
626 v3f( BS / 2, -BS / 2, -BS / 2),
627 v3f(-BS / 2, -BS / 2, -BS / 2),
630 for (v3f &vertex : vertices) {
633 vertex.rotateXZBy(180); break;
635 vertex.rotateYZBy( 90); break;
637 vertex.rotateXZBy( 90); break;
639 vertex.rotateXZBy( 0); break;
641 vertex.rotateYZBy(-90); break;
643 vertex.rotateXZBy(-90); break;
646 drawQuad(vertices, dir);
650 void MapblockMeshGenerator::drawGlasslikeFramedNode()
653 for (int face = 0; face < 6; face++)
654 getTile(g_6dirs[face], &tiles[face]);
656 TileSpec glass_tiles[6];
657 if (tiles[1].layers[0].texture &&
658 tiles[2].layers[0].texture &&
659 tiles[3].layers[0].texture) {
660 glass_tiles[0] = tiles[4];
661 glass_tiles[1] = tiles[0];
662 glass_tiles[2] = tiles[4];
663 glass_tiles[3] = tiles[4];
664 glass_tiles[4] = tiles[3];
665 glass_tiles[5] = tiles[4];
667 for (auto &glass_tile : glass_tiles)
668 glass_tile = tiles[4];
671 u8 param2 = n.getParam2();
672 bool H_merge = !(param2 & 128);
673 bool V_merge = !(param2 & 64);
676 static const float a = BS / 2;
677 static const float g = a - 0.003;
678 static const float b = .876 * ( BS / 2 );
680 static const aabb3f frame_edges[FRAMED_EDGE_COUNT] = {
681 aabb3f( b, b, -a, a, a, a), // y+
682 aabb3f(-a, b, -a, -b, a, a), // y+
683 aabb3f( b, -a, -a, a, -b, a), // y-
684 aabb3f(-a, -a, -a, -b, -b, a), // y-
685 aabb3f( b, -a, b, a, a, a), // x+
686 aabb3f( b, -a, -a, a, a, -b), // x+
687 aabb3f(-a, -a, b, -b, a, a), // x-
688 aabb3f(-a, -a, -a, -b, a, -b), // x-
689 aabb3f(-a, b, b, a, a, a), // z+
690 aabb3f(-a, -a, b, a, -b, a), // z+
691 aabb3f(-a, -a, -a, a, -b, -b), // z-
692 aabb3f(-a, b, -a, a, a, -b), // z-
694 static const aabb3f glass_faces[6] = {
695 aabb3f(-g, -g, g, g, g, g), // z+
696 aabb3f(-g, g, -g, g, g, g), // y+
697 aabb3f( g, -g, -g, g, g, g), // x+
698 aabb3f(-g, -g, -g, g, g, -g), // z-
699 aabb3f(-g, -g, -g, g, -g, g), // y-
700 aabb3f(-g, -g, -g, -g, g, g), // x-
703 // tables of neighbour (connect if same type and merge allowed),
704 // checked with g_26dirs
706 // 1 = connect, 0 = face visible
707 bool nb[FRAMED_NEIGHBOR_COUNT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
710 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};
711 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};
712 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};
713 const bool *check_nb = check_nb_all;
715 // neighbours checks for frames visibility
716 if (H_merge || V_merge) {
718 check_nb = check_nb_vertical; // vertical-only merge
720 check_nb = check_nb_horizontal; // horizontal-only merge
721 content_t current = n.getContent();
722 for (int i = 0; i < FRAMED_NEIGHBOR_COUNT; i++) {
725 v3s16 n2p = blockpos_nodes + p + g_26dirs[i];
726 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
727 content_t n2c = n2.getContent();
735 static const u8 nb_triplet[FRAMED_EDGE_COUNT][3] = {
736 {1, 2, 7}, {1, 5, 6}, {4, 2, 15}, {4, 5, 14},
737 {2, 0, 11}, {2, 3, 13}, {5, 0, 10}, {5, 3, 12},
738 {0, 1, 8}, {0, 4, 16}, {3, 4, 17}, {3, 1, 9},
742 for (int edge = 0; edge < FRAMED_EDGE_COUNT; edge++) {
744 if (nb[nb_triplet[edge][2]])
745 edge_invisible = nb[nb_triplet[edge][0]] & nb[nb_triplet[edge][1]];
747 edge_invisible = nb[nb_triplet[edge][0]] ^ nb[nb_triplet[edge][1]];
750 drawAutoLightedCuboid(frame_edges[edge]);
753 for (int face = 0; face < 6; face++) {
756 tile = glass_tiles[face];
757 drawAutoLightedCuboid(glass_faces[face]);
760 // Optionally render internal liquid level defined by param2
761 // Liquid is textured with 1 tile defined in nodedef 'special_tiles'
762 if (param2 > 0 && f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL &&
763 f->special_tiles[0].layers[0].texture) {
764 // Internal liquid level has param2 range 0 .. 63,
765 // convert it to -0.5 .. 0.5
766 float vlev = (param2 / 63.0) * 2.0 - 1.0;
767 getSpecialTile(0, &tile);
768 drawAutoLightedCuboid(aabb3f(-(nb[5] ? g : b),
772 (nb[1] ? g : b) * vlev,
777 void MapblockMeshGenerator::drawAllfacesNode()
779 static const aabb3f box(-BS / 2, -BS / 2, -BS / 2, BS / 2, BS / 2, BS / 2);
781 drawAutoLightedCuboid(box);
784 void MapblockMeshGenerator::drawTorchlikeNode()
786 u8 wall = n.getWallMounted(nodedef);
789 case DWM_YP: tileindex = 1; break; // ceiling
790 case DWM_YN: tileindex = 0; break; // floor
791 default: tileindex = 2; // side (or invalid—should we care?)
793 useTile(tileindex, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
795 float size = BS / 2 * f->visual_scale;
799 v3f( size, -size, 0),
800 v3f(-size, -size, 0),
803 for (v3f &vertex : vertices) {
806 vertex.rotateXZBy(-45); break;
808 vertex.rotateXZBy( 45); break;
810 vertex.rotateXZBy( 0); break;
812 vertex.rotateXZBy(180); break;
814 vertex.rotateXZBy( 90); break;
816 vertex.rotateXZBy(-90); break;
822 void MapblockMeshGenerator::drawSignlikeNode()
824 u8 wall = n.getWallMounted(nodedef);
825 useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
826 static const float offset = BS / 16;
827 float size = BS / 2 * f->visual_scale;
828 // Wall at X+ of node
830 v3f(BS / 2 - offset, size, size),
831 v3f(BS / 2 - offset, size, -size),
832 v3f(BS / 2 - offset, -size, -size),
833 v3f(BS / 2 - offset, -size, size),
836 for (v3f &vertex : vertices) {
839 vertex.rotateXYBy( 90); break;
841 vertex.rotateXYBy(-90); break;
843 vertex.rotateXZBy( 0); break;
845 vertex.rotateXZBy(180); break;
847 vertex.rotateXZBy( 90); break;
849 vertex.rotateXZBy(-90); break;
855 void MapblockMeshGenerator::drawPlantlikeQuad(float rotation, float quad_offset,
856 bool offset_top_only)
859 v3f(-scale, -BS / 2 + 2.0 * scale * plant_height, 0),
860 v3f( scale, -BS / 2 + 2.0 * scale * plant_height, 0),
861 v3f( scale, -BS / 2, 0),
862 v3f(-scale, -BS / 2, 0),
864 if (random_offset_Y) {
865 PseudoRandom yrng(face_num++ | p.X << 16 | p.Z << 8 | p.Y << 24);
866 offset.Y = -BS * ((yrng.next() % 16 / 16.0) * 0.125);
868 int offset_count = offset_top_only ? 2 : 4;
869 for (int i = 0; i < offset_count; i++)
870 vertices[i].Z += quad_offset;
872 for (v3f &vertex : vertices) {
873 vertex.rotateXZBy(rotation + rotate_degree);
876 drawQuad(vertices, v3s16(0, 0, 0), plant_height);
879 void MapblockMeshGenerator::drawPlantlike()
881 draw_style = PLANT_STYLE_CROSS;
882 scale = BS / 2 * f->visual_scale;
883 offset = v3f(0, 0, 0);
885 random_offset_Y = false;
889 switch (f->param_type_2) {
890 case CPT2_MESHOPTIONS:
891 draw_style = PlantlikeStyle(n.param2 & MO_MASK_STYLE);
892 if (n.param2 & MO_BIT_SCALE_SQRT2)
894 if (n.param2 & MO_BIT_RANDOM_OFFSET) {
895 PseudoRandom rng(p.X << 8 | p.Z | p.Y << 16);
896 offset.X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
897 offset.Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
899 if (n.param2 & MO_BIT_RANDOM_OFFSET_Y)
900 random_offset_Y = true;
904 rotate_degree = n.param2 * 2;
908 plant_height = n.param2 / 16.0;
915 switch (draw_style) {
916 case PLANT_STYLE_CROSS:
917 drawPlantlikeQuad(46);
918 drawPlantlikeQuad(-44);
921 case PLANT_STYLE_CROSS2:
922 drawPlantlikeQuad(91);
923 drawPlantlikeQuad(1);
926 case PLANT_STYLE_STAR:
927 drawPlantlikeQuad(121);
928 drawPlantlikeQuad(241);
929 drawPlantlikeQuad(1);
932 case PLANT_STYLE_HASH:
933 drawPlantlikeQuad( 1, BS / 4);
934 drawPlantlikeQuad( 91, BS / 4);
935 drawPlantlikeQuad(181, BS / 4);
936 drawPlantlikeQuad(271, BS / 4);
939 case PLANT_STYLE_HASH2:
940 drawPlantlikeQuad( 1, -BS / 2, true);
941 drawPlantlikeQuad( 91, -BS / 2, true);
942 drawPlantlikeQuad(181, -BS / 2, true);
943 drawPlantlikeQuad(271, -BS / 2, true);
948 void MapblockMeshGenerator::drawPlantlikeNode()
954 void MapblockMeshGenerator::drawPlantlikeRootedNode()
956 useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, 0, true);
957 origin += v3f(0.0, BS, 0.0);
959 if (data->m_smooth_lighting) {
960 getSmoothLightFrame();
962 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
963 light = getInteriorLight(ntop, 1, nodedef);
969 void MapblockMeshGenerator::drawFirelikeQuad(float rotation, float opening_angle,
970 float offset_h, float offset_v)
973 v3f(-scale, -BS / 2 + scale * 2, 0),
974 v3f( scale, -BS / 2 + scale * 2, 0),
975 v3f( scale, -BS / 2, 0),
976 v3f(-scale, -BS / 2, 0),
979 for (v3f &vertex : vertices) {
980 vertex.rotateYZBy(opening_angle);
981 vertex.Z += offset_h;
982 vertex.rotateXZBy(rotation);
983 vertex.Y += offset_v;
988 void MapblockMeshGenerator::drawFirelikeNode()
991 scale = BS / 2 * f->visual_scale;
993 // Check for adjacent nodes
994 bool neighbors = false;
995 bool neighbor[6] = {0, 0, 0, 0, 0, 0};
996 content_t current = n.getContent();
997 for (int i = 0; i < 6; i++) {
998 v3s16 n2p = blockpos_nodes + p + g_6dirs[i];
999 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
1000 content_t n2c = n2.getContent();
1001 if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
1006 bool drawBasicFire = neighbor[D6D_YN] || !neighbors;
1007 bool drawBottomFire = neighbor[D6D_YP];
1009 if (drawBasicFire || neighbor[D6D_ZP])
1010 drawFirelikeQuad(0, -10, 0.4 * BS);
1011 else if (drawBottomFire)
1012 drawFirelikeQuad(0, 70, 0.47 * BS, 0.484 * BS);
1014 if (drawBasicFire || neighbor[D6D_XN])
1015 drawFirelikeQuad(90, -10, 0.4 * BS);
1016 else if (drawBottomFire)
1017 drawFirelikeQuad(90, 70, 0.47 * BS, 0.484 * BS);
1019 if (drawBasicFire || neighbor[D6D_ZN])
1020 drawFirelikeQuad(180, -10, 0.4 * BS);
1021 else if (drawBottomFire)
1022 drawFirelikeQuad(180, 70, 0.47 * BS, 0.484 * BS);
1024 if (drawBasicFire || neighbor[D6D_XP])
1025 drawFirelikeQuad(270, -10, 0.4 * BS);
1026 else if (drawBottomFire)
1027 drawFirelikeQuad(270, 70, 0.47 * BS, 0.484 * BS);
1029 if (drawBasicFire) {
1030 drawFirelikeQuad(45, 0, 0.0);
1031 drawFirelikeQuad(-45, 0, 0.0);
1035 void MapblockMeshGenerator::drawFencelikeNode()
1038 TileSpec tile_nocrack = tile;
1040 for (auto &layer : tile_nocrack.layers)
1041 layer.material_flags &= ~MATERIAL_FLAG_CRACK;
1043 // Put wood the right way around in the posts
1044 TileSpec tile_rot = tile;
1045 tile_rot.rotation = 1;
1047 static const f32 post_rad = BS / 8;
1048 static const f32 bar_rad = BS / 16;
1049 static const f32 bar_len = BS / 2 - post_rad;
1051 // The post - always present
1052 static const aabb3f post(-post_rad, -BS / 2, -post_rad,
1053 post_rad, BS / 2, post_rad);
1054 static const f32 postuv[24] = {
1055 0.375, 0.375, 0.625, 0.625,
1056 0.375, 0.375, 0.625, 0.625,
1057 0.000, 0.000, 0.250, 1.000,
1058 0.250, 0.000, 0.500, 1.000,
1059 0.500, 0.000, 0.750, 1.000,
1060 0.750, 0.000, 1.000, 1.000,
1063 drawAutoLightedCuboid(post, postuv);
1065 tile = tile_nocrack;
1067 // Now a section of fence, +X, if there's a post there
1070 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1071 const ContentFeatures *f2 = &nodedef->get(n2);
1072 if (f2->drawtype == NDT_FENCELIKE) {
1073 static const aabb3f bar_x1(BS / 2 - bar_len, BS / 4 - bar_rad, -bar_rad,
1074 BS / 2 + bar_len, BS / 4 + bar_rad, bar_rad);
1075 static const aabb3f bar_x2(BS / 2 - bar_len, -BS / 4 - bar_rad, -bar_rad,
1076 BS / 2 + bar_len, -BS / 4 + bar_rad, bar_rad);
1077 static const f32 xrailuv[24] = {
1078 0.000, 0.125, 1.000, 0.250,
1079 0.000, 0.250, 1.000, 0.375,
1080 0.375, 0.375, 0.500, 0.500,
1081 0.625, 0.625, 0.750, 0.750,
1082 0.000, 0.500, 1.000, 0.625,
1083 0.000, 0.875, 1.000, 1.000,
1085 drawAutoLightedCuboid(bar_x1, xrailuv);
1086 drawAutoLightedCuboid(bar_x2, xrailuv);
1089 // Now a section of fence, +Z, if there's a post there
1092 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1093 f2 = &nodedef->get(n2);
1094 if (f2->drawtype == NDT_FENCELIKE) {
1095 static const aabb3f bar_z1(-bar_rad, BS / 4 - bar_rad, BS / 2 - bar_len,
1096 bar_rad, BS / 4 + bar_rad, BS / 2 + bar_len);
1097 static const aabb3f bar_z2(-bar_rad, -BS / 4 - bar_rad, BS / 2 - bar_len,
1098 bar_rad, -BS / 4 + bar_rad, BS / 2 + bar_len);
1099 static const f32 zrailuv[24] = {
1100 0.1875, 0.0625, 0.3125, 0.3125, // cannot rotate; stretch
1101 0.2500, 0.0625, 0.3750, 0.3125, // for wood texture instead
1102 0.0000, 0.5625, 1.0000, 0.6875,
1103 0.0000, 0.3750, 1.0000, 0.5000,
1104 0.3750, 0.3750, 0.5000, 0.5000,
1105 0.6250, 0.6250, 0.7500, 0.7500,
1107 drawAutoLightedCuboid(bar_z1, zrailuv);
1108 drawAutoLightedCuboid(bar_z2, zrailuv);
1112 bool MapblockMeshGenerator::isSameRail(v3s16 dir)
1114 MapNode node2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
1115 if (node2.getContent() == n.getContent())
1117 const ContentFeatures &def2 = nodedef->get(node2);
1118 return ((def2.drawtype == NDT_RAILLIKE) &&
1119 (def2.getGroup(raillike_groupname) == raillike_group));
1122 void MapblockMeshGenerator::drawRaillikeNode()
1124 static const v3s16 direction[4] = {
1130 static const int slope_angle[4] = {0, 180, 90, -90};
1142 static const RailDesc rail_kinds[16] = {
1145 {straight, 0}, // . . . .
1146 {straight, 0}, // . . . +Z
1147 {straight, 0}, // . . -Z .
1148 {straight, 0}, // . . -Z +Z
1149 {straight, 90}, // . -X . .
1150 { curved, 180}, // . -X . +Z
1151 { curved, 270}, // . -X -Z .
1152 {junction, 180}, // . -X -Z +Z
1153 {straight, 90}, // +X . . .
1154 { curved, 90}, // +X . . +Z
1155 { curved, 0}, // +X . -Z .
1156 {junction, 0}, // +X . -Z +Z
1157 {straight, 90}, // +X -X . .
1158 {junction, 90}, // +X -X . +Z
1159 {junction, 270}, // +X -X -Z .
1160 { cross, 0}, // +X -X -Z +Z
1163 raillike_group = nodedef->get(n).getGroup(raillike_groupname);
1168 bool sloped = false;
1169 for (int dir = 0; dir < 4; dir++) {
1170 bool rail_above = isSameRail(direction[dir] + v3s16(0, 1, 0));
1173 angle = slope_angle[dir];
1176 isSameRail(direction[dir]) ||
1177 isSameRail(direction[dir] + v3s16(0, -1, 0)))
1182 tile_index = straight;
1184 tile_index = rail_kinds[code].tile_index;
1185 angle = rail_kinds[code].angle;
1188 useTile(tile_index, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
1190 static const float offset = BS / 64;
1191 static const float size = BS / 2;
1192 float y2 = sloped ? size : -size;
1194 v3f(-size, y2 + offset, size),
1195 v3f( size, y2 + offset, size),
1196 v3f( size, -size + offset, -size),
1197 v3f(-size, -size + offset, -size),
1200 for (v3f &vertex : vertices)
1201 vertex.rotateXZBy(angle);
1205 void MapblockMeshGenerator::drawNodeboxNode()
1207 static const v3s16 tile_dirs[6] = {
1216 // we have this order for some reason...
1217 static const v3s16 connection_dirs[6] = {
1218 v3s16( 0, 1, 0), // top
1219 v3s16( 0, -1, 0), // bottom
1220 v3s16( 0, 0, -1), // front
1221 v3s16(-1, 0, 0), // left
1222 v3s16( 0, 0, 1), // back
1223 v3s16( 1, 0, 0), // right
1227 for (int face = 0; face < 6; face++) {
1228 // Handles facedir rotation for textures
1229 getTile(tile_dirs[face], &tiles[face]);
1232 // locate possible neighboring nodes to connect to
1233 int neighbors_set = 0;
1234 if (f->node_box.type == NODEBOX_CONNECTED) {
1235 for (int dir = 0; dir != 6; dir++) {
1236 int flag = 1 << dir;
1237 v3s16 p2 = blockpos_nodes + p + connection_dirs[dir];
1238 MapNode n2 = data->m_vmanip.getNodeNoEx(p2);
1239 if (nodedef->nodeboxConnects(n, n2, flag))
1240 neighbors_set |= flag;
1244 std::vector<aabb3f> boxes;
1245 n.getNodeBoxes(nodedef, &boxes, neighbors_set);
1246 for (const auto &box : boxes)
1247 drawAutoLightedCuboid(box, NULL, tiles, 6);
1250 void MapblockMeshGenerator::drawMeshNode()
1254 bool private_mesh; // as a grab/drop pair is not thread-safe
1256 if (f->param_type_2 == CPT2_FACEDIR ||
1257 f->param_type_2 == CPT2_COLORED_FACEDIR) {
1258 facedir = n.getFaceDir(nodedef);
1259 } else if (f->param_type_2 == CPT2_WALLMOUNTED ||
1260 f->param_type_2 == CPT2_COLORED_WALLMOUNTED) {
1261 // Convert wallmounted to 6dfacedir.
1262 // When cache enabled, it is already converted.
1263 facedir = n.getWallMounted(nodedef);
1264 if (!enable_mesh_cache)
1265 facedir = wallmounted_to_facedir[facedir];
1268 if (!data->m_smooth_lighting && f->mesh_ptr[facedir]) {
1269 // use cached meshes
1270 private_mesh = false;
1271 mesh = f->mesh_ptr[facedir];
1272 } else if (f->mesh_ptr[0]) {
1273 // no cache, clone and rotate mesh
1274 private_mesh = true;
1275 mesh = cloneMesh(f->mesh_ptr[0]);
1276 rotateMeshBy6dFacedir(mesh, facedir);
1277 recalculateBoundingBox(mesh);
1278 meshmanip->recalculateNormals(mesh, true, false);
1282 int mesh_buffer_count = mesh->getMeshBufferCount();
1283 for (int j = 0; j < mesh_buffer_count; j++) {
1285 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1286 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1287 int vertex_count = buf->getVertexCount();
1289 if (data->m_smooth_lighting) {
1290 // Mesh is always private here. So the lighting is applied to each
1291 // vertex right here.
1292 for (int k = 0; k < vertex_count; k++) {
1293 video::S3DVertex &vertex = vertices[k];
1294 vertex.Color = blendLightColor(vertex.Pos, vertex.Normal);
1295 vertex.Pos += origin;
1297 collector->append(tile, vertices, vertex_count,
1298 buf->getIndices(), buf->getIndexCount());
1300 // Don't modify the mesh, it may not be private here.
1301 // Instead, let the collector process colors, etc.
1302 collector->append(tile, vertices, vertex_count,
1303 buf->getIndices(), buf->getIndexCount(), origin,
1304 color, f->light_source);
1311 // also called when the drawtype is known but should have been pre-converted
1312 void MapblockMeshGenerator::errorUnknownDrawtype()
1314 infostream << "Got drawtype " << f->drawtype << std::endl;
1315 FATAL_ERROR("Unknown drawtype");
1318 void MapblockMeshGenerator::drawNode()
1320 // skip some drawtypes early
1321 switch (f->drawtype) {
1322 case NDT_NORMAL: // Drawn by MapBlockMesh
1323 case NDT_AIRLIKE: // Not drawn at all
1324 case NDT_LIQUID: // Drawn by MapBlockMesh
1329 origin = intToFloat(p, BS);
1330 if (data->m_smooth_lighting)
1331 getSmoothLightFrame();
1333 light = getInteriorLight(n, 1, nodedef);
1334 switch (f->drawtype) {
1335 case NDT_FLOWINGLIQUID: drawLiquidNode(); break;
1336 case NDT_GLASSLIKE: drawGlasslikeNode(); break;
1337 case NDT_GLASSLIKE_FRAMED: drawGlasslikeFramedNode(); break;
1338 case NDT_ALLFACES: drawAllfacesNode(); break;
1339 case NDT_TORCHLIKE: drawTorchlikeNode(); break;
1340 case NDT_SIGNLIKE: drawSignlikeNode(); break;
1341 case NDT_PLANTLIKE: drawPlantlikeNode(); break;
1342 case NDT_PLANTLIKE_ROOTED: drawPlantlikeRootedNode(); break;
1343 case NDT_FIRELIKE: drawFirelikeNode(); break;
1344 case NDT_FENCELIKE: drawFencelikeNode(); break;
1345 case NDT_RAILLIKE: drawRaillikeNode(); break;
1346 case NDT_NODEBOX: drawNodeboxNode(); break;
1347 case NDT_MESH: drawMeshNode(); break;
1348 default: errorUnknownDrawtype(); break;
1353 TODO: Fix alpha blending for special nodes
1354 Currently only the last element rendered is blended correct
1356 void MapblockMeshGenerator::generate()
1358 for (p.Z = 0; p.Z < MAP_BLOCKSIZE; p.Z++)
1359 for (p.Y = 0; p.Y < MAP_BLOCKSIZE; p.Y++)
1360 for (p.X = 0; p.X < MAP_BLOCKSIZE; p.X++) {
1361 n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1362 f = &nodedef->get(n);
1367 void MapblockMeshGenerator::renderSingle(content_t node)
1370 n = MapNode(node, 0xff, 0x00);
1371 f = &nodedef->get(n);