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>
32 #include "util/cpp11.h"
34 // Distance of light extrapolation (for oversized nodes)
35 // After this distance, it gives up and considers light level constant
36 #define SMOOTH_LIGHTING_OVERSIZE 1.0
38 // Node edge count (for glasslike-framed)
39 #define FRAMED_EDGE_COUNT 12
41 // Node neighbor count, including edge-connected, but not vertex-connected
42 // (for glasslike-framed)
43 // Corresponding offsets are listed in g_27dirs
44 #define FRAMED_NEIGHBOR_COUNT 18
46 static constexpr v3s16 light_dirs[8] = {
57 // Standard index set to make a quad on 4 vertices
58 static constexpr u16 quad_indices[] = {0, 1, 2, 2, 3, 0};
60 const std::string MapblockMeshGenerator::raillike_groupname = "connect_to_raillike";
62 MapblockMeshGenerator::MapblockMeshGenerator(MeshMakeData *input, MeshCollector *output)
67 nodedef = data->m_client->ndef();
68 smgr = data->m_client->getSceneManager();
69 meshmanip = smgr->getMeshManipulator();
71 enable_mesh_cache = g_settings->getBool("enable_mesh_cache") &&
72 !data->m_smooth_lighting; // Mesh cache is not supported with smooth lighting
74 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
77 void MapblockMeshGenerator::useTile(int index, bool disable_backface_culling)
79 getNodeTileN(n, p, index, data, tile);
80 if (!data->m_smooth_lighting)
81 color = encode_light(light, f->light_source);
82 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
83 tile.layers[layer].material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
84 if (disable_backface_culling)
85 tile.layers[layer].material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
89 void MapblockMeshGenerator::useDefaultTile(bool set_color)
91 getNodeTile(n, p, v3s16(0, 0, 0), data, tile);
92 if (set_color && !data->m_smooth_lighting)
93 color = encode_light(light, f->light_source);
96 void MapblockMeshGenerator::getTile(const v3s16& direction, TileSpec &tile)
98 getNodeTile(n, p, direction, data, tile);
101 void MapblockMeshGenerator::drawQuad(v3f *coords, const v3s16 &normal)
103 static const v2f tcoords[4] = {v2f(0, 0), v2f(1, 0), v2f(1, 1), v2f(0, 1)};
104 video::S3DVertex vertices[4];
105 bool shade_face = !f->light_source && (normal != v3s16(0, 0, 0));
106 v3f normal2(normal.X, normal.Y, normal.Z);
107 for (int j = 0; j < 4; j++) {
108 vertices[j].Pos = coords[j] + origin;
109 vertices[j].Normal = normal2;
110 if (data->m_smooth_lighting)
111 vertices[j].Color = blendLightColor(coords[j]);
113 vertices[j].Color = color;
115 applyFacesShading(vertices[j].Color, normal2);
116 vertices[j].TCoords = tcoords[j];
118 collector->append(tile, vertices, 4, quad_indices, 6);
122 // tiles - the tiles (materials) to use (for all 6 faces)
123 // tilecount - number of entries in tiles, 1<=tilecount<=6
124 // lights - vertex light levels. The order is the same as in light_dirs.
125 // NULL may be passed if smooth lighting is disabled.
126 // txc - texture coordinates - this is a list of texture coordinates
127 // for the opposite corners of each face - therefore, there
128 // should be (2+2)*6=24 values in the list. The order of
129 // the faces in the list is up-down-right-left-back-front
130 // (compatible with ContentFeatures).
131 void MapblockMeshGenerator::drawCuboid(const aabb3f &box,
132 TileSpec *tiles, int tilecount, const u16 *lights, const f32 *txc)
134 assert(tilecount >= 1 && tilecount <= 6); // pre-condition
136 v3f min = box.MinEdge;
137 v3f max = box.MaxEdge;
139 video::SColor colors[6];
140 if (!data->m_smooth_lighting) {
141 for (int face = 0; face != 6; ++face) {
142 colors[face] = encode_light(light, f->light_source);
144 if (!f->light_source) {
145 applyFacesShading(colors[0], v3f(0, 1, 0));
146 applyFacesShading(colors[1], v3f(0, -1, 0));
147 applyFacesShading(colors[2], v3f(1, 0, 0));
148 applyFacesShading(colors[3], v3f(-1, 0, 0));
149 applyFacesShading(colors[4], v3f(0, 0, 1));
150 applyFacesShading(colors[5], v3f(0, 0, -1));
154 video::S3DVertex vertices[24] = {
156 video::S3DVertex(min.X, max.Y, max.Z, 0, 1, 0, colors[0], txc[0], txc[1]),
157 video::S3DVertex(max.X, max.Y, max.Z, 0, 1, 0, colors[0], txc[2], txc[1]),
158 video::S3DVertex(max.X, max.Y, min.Z, 0, 1, 0, colors[0], txc[2], txc[3]),
159 video::S3DVertex(min.X, max.Y, min.Z, 0, 1, 0, colors[0], txc[0], txc[3]),
161 video::S3DVertex(min.X, min.Y, min.Z, 0, -1, 0, colors[1], txc[4], txc[5]),
162 video::S3DVertex(max.X, min.Y, min.Z, 0, -1, 0, colors[1], txc[6], txc[5]),
163 video::S3DVertex(max.X, min.Y, max.Z, 0, -1, 0, colors[1], txc[6], txc[7]),
164 video::S3DVertex(min.X, min.Y, max.Z, 0, -1, 0, colors[1], txc[4], txc[7]),
166 video::S3DVertex(max.X, max.Y, min.Z, 1, 0, 0, colors[2], txc[ 8], txc[9]),
167 video::S3DVertex(max.X, max.Y, max.Z, 1, 0, 0, colors[2], txc[10], txc[9]),
168 video::S3DVertex(max.X, min.Y, max.Z, 1, 0, 0, colors[2], txc[10], txc[11]),
169 video::S3DVertex(max.X, min.Y, min.Z, 1, 0, 0, colors[2], txc[ 8], txc[11]),
171 video::S3DVertex(min.X, max.Y, max.Z, -1, 0, 0, colors[3], txc[12], txc[13]),
172 video::S3DVertex(min.X, max.Y, min.Z, -1, 0, 0, colors[3], txc[14], txc[13]),
173 video::S3DVertex(min.X, min.Y, min.Z, -1, 0, 0, colors[3], txc[14], txc[15]),
174 video::S3DVertex(min.X, min.Y, max.Z, -1, 0, 0, colors[3], txc[12], txc[15]),
176 video::S3DVertex(max.X, max.Y, max.Z, 0, 0, 1, colors[4], txc[16], txc[17]),
177 video::S3DVertex(min.X, max.Y, max.Z, 0, 0, 1, colors[4], txc[18], txc[17]),
178 video::S3DVertex(min.X, min.Y, max.Z, 0, 0, 1, colors[4], txc[18], txc[19]),
179 video::S3DVertex(max.X, min.Y, max.Z, 0, 0, 1, colors[4], txc[16], txc[19]),
181 video::S3DVertex(min.X, max.Y, min.Z, 0, 0, -1, colors[5], txc[20], txc[21]),
182 video::S3DVertex(max.X, max.Y, min.Z, 0, 0, -1, colors[5], txc[22], txc[21]),
183 video::S3DVertex(max.X, min.Y, min.Z, 0, 0, -1, colors[5], txc[22], txc[23]),
184 video::S3DVertex(min.X, min.Y, min.Z, 0, 0, -1, colors[5], txc[20], txc[23]),
187 static const u8 light_indices[24] = {
196 for (int face = 0; face < 6; face++) {
197 int tileindex = MYMIN(face, tilecount - 1);
198 const TileSpec &tile = tiles[tileindex];
199 for (int j = 0; j < 4; j++) {
200 video::S3DVertex &vertex = vertices[face * 4 + j];
201 v2f &tcoords = vertex.TCoords;
202 switch (tile.rotation) {
206 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
209 tcoords.rotateBy(180, irr::core::vector2df(0, 0));
212 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
215 tcoords.X = 1.0 - tcoords.X;
216 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
219 tcoords.X = 1.0 - tcoords.X;
220 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
223 tcoords.Y = 1.0 - tcoords.Y;
224 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
227 tcoords.Y = 1.0 - tcoords.Y;
228 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
231 tcoords.X = 1.0 - tcoords.X;
234 tcoords.Y = 1.0 - tcoords.Y;
242 if (data->m_smooth_lighting) {
243 for (int j = 0; j < 24; ++j) {
244 vertices[j].Color = encode_light(lights[light_indices[j]],
246 if (!f->light_source)
247 applyFacesShading(vertices[j].Color, vertices[j].Normal);
251 // Add to mesh collector
252 for (int k = 0; k < 6; ++k) {
253 int tileindex = MYMIN(k, tilecount - 1);
254 collector->append(tiles[tileindex], vertices + 4 * k, 4, quad_indices, 6);
258 // Gets the base lighting values for a node
259 void MapblockMeshGenerator::getSmoothLightFrame()
261 for (int k = 0; k < 8; ++k) {
262 u16 light = getSmoothLight(blockpos_nodes + p, light_dirs[k], data);
263 frame.lightsA[k] = light & 0xff;
264 frame.lightsB[k] = light >> 8;
268 // Calculates vertex light level
269 // vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
270 u16 MapblockMeshGenerator::blendLight(const v3f &vertex_pos)
272 f32 x = core::clamp(vertex_pos.X / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
273 f32 y = core::clamp(vertex_pos.Y / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
274 f32 z = core::clamp(vertex_pos.Z / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
277 for (int k = 0; k < 8; ++k) {
278 f32 dx = (k & 4) ? x : 1 - x;
279 f32 dy = (k & 2) ? y : 1 - y;
280 f32 dz = (k & 1) ? z : 1 - z;
281 lightA += dx * dy * dz * frame.lightsA[k];
282 lightB += dx * dy * dz * frame.lightsB[k];
285 core::clamp(core::round32(lightA), 0, 255) |
286 core::clamp(core::round32(lightB), 0, 255) << 8;
289 // Calculates vertex color to be used in mapblock mesh
290 // vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
291 // tile_color - node's tile color
292 video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos)
294 u16 light = blendLight(vertex_pos);
295 return encode_light(light, f->light_source);
298 video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos,
299 const v3f &vertex_normal)
301 video::SColor color = blendLightColor(vertex_pos);
302 if (!f->light_source)
303 applyFacesShading(color, vertex_normal);
307 void MapblockMeshGenerator::generateCuboidTextureCoords(const aabb3f &box, f32 *coords)
309 f32 tx1 = (box.MinEdge.X / BS) + 0.5;
310 f32 ty1 = (box.MinEdge.Y / BS) + 0.5;
311 f32 tz1 = (box.MinEdge.Z / BS) + 0.5;
312 f32 tx2 = (box.MaxEdge.X / BS) + 0.5;
313 f32 ty2 = (box.MaxEdge.Y / BS) + 0.5;
314 f32 tz2 = (box.MaxEdge.Z / BS) + 0.5;
316 tx1, 1 - tz2, tx2, 1 - tz1, // up
317 tx1, tz1, tx2, tz2, // down
318 tz1, 1 - ty2, tz2, 1 - ty1, // right
319 1 - tz2, 1 - ty2, 1 - tz1, 1 - ty1, // left
320 1 - tx2, 1 - ty2, 1 - tx1, 1 - ty1, // back
321 tx1, 1 - ty2, tx2, 1 - ty1, // front
323 for (int i = 0; i != 24; ++i)
327 void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc,
328 TileSpec *tiles, int tile_count)
330 f32 texture_coord_buf[24];
331 f32 dx1 = box.MinEdge.X;
332 f32 dy1 = box.MinEdge.Y;
333 f32 dz1 = box.MinEdge.Z;
334 f32 dx2 = box.MaxEdge.X;
335 f32 dy2 = box.MaxEdge.Y;
336 f32 dz2 = box.MaxEdge.Z;
337 box.MinEdge += origin;
338 box.MaxEdge += origin;
340 generateCuboidTextureCoords(box, texture_coord_buf);
341 txc = texture_coord_buf;
347 if (data->m_smooth_lighting) {
349 for (int j = 0; j < 8; ++j) {
351 d.X = (j & 4) ? dx2 : dx1;
352 d.Y = (j & 2) ? dy2 : dy1;
353 d.Z = (j & 1) ? dz2 : dz1;
354 lights[j] = blendLight(d);
356 drawCuboid(box, tiles, tile_count, lights, txc);
358 drawCuboid(box, tiles, tile_count, NULL, txc);
363 * Returns the i-th special tile for a map node.
365 static TileSpec getSpecialTile(const ContentFeatures &f,
366 const MapNode &n, u8 i)
368 TileSpec copy = f.special_tiles[i];
369 for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
370 TileLayer *layer = ©.layers[layernum];
371 if (layer->texture_id == 0)
373 if (!layer->has_color)
374 n.getColor(f, &(layer->color));
379 void MapblockMeshGenerator::prepareLiquidNodeDrawing(bool flowing)
381 tile_liquid_top = getSpecialTile(*f, n, 0);
382 tile_liquid = getSpecialTile(*f, n, flowing ? 1 : 0);
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(bool flowing)
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 // Skip getting unneeded data
413 if (!flowing && u && w)
416 NeighborData &neighbor = liquid_neighbors[w + 1][u + 1];
417 v3s16 p2 = p + v3s16(u, 0, w);
418 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
419 neighbor.content = n2.getContent();
420 neighbor.level = -0.5 * BS;
421 neighbor.is_same_liquid = false;
422 neighbor.top_is_same_liquid = false;
424 if (neighbor.content == CONTENT_IGNORE)
427 if (neighbor.content == c_source) {
428 neighbor.is_same_liquid = true;
429 neighbor.level = 0.5 * BS;
430 } else if (neighbor.content == c_flowing) {
431 neighbor.is_same_liquid = true;
432 u8 liquid_level = (n2.param2 & LIQUID_LEVEL_MASK);
433 if (liquid_level <= LIQUID_LEVEL_MAX + 1 - range)
436 liquid_level -= (LIQUID_LEVEL_MAX + 1 - range);
437 neighbor.level = (-0.5 + (liquid_level + 0.5) / range) * BS;
440 // Check node above neighbor.
441 // NOTE: This doesn't get executed if neighbor
444 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
445 if (n2.getContent() == c_source || n2.getContent() == c_flowing)
446 neighbor.top_is_same_liquid = true;
450 void MapblockMeshGenerator::resetCornerLevels()
452 for (int k = 0; k < 2; k++)
453 for (int i = 0; i < 2; i++)
454 corner_levels[k][i] = 0.5 * BS;
457 void MapblockMeshGenerator::calculateCornerLevels()
459 for (int k = 0; k < 2; k++)
460 for (int i = 0; i < 2; i++)
461 corner_levels[k][i] = getCornerLevel(i, k);
464 f32 MapblockMeshGenerator::getCornerLevel(int i, int k)
469 for (int dk = 0; dk < 2; dk++)
470 for (int di = 0; di < 2; di++) {
471 NeighborData &neighbor_data = liquid_neighbors[k + dk][i + di];
472 content_t content = neighbor_data.content;
474 // If top is liquid, draw starting from top of node
475 if (neighbor_data.top_is_same_liquid)
478 // Source always has the full height
479 if (content == c_source)
482 // Flowing liquid has level information
483 if (content == c_flowing) {
484 sum += neighbor_data.level;
486 } else if (content == CONTENT_AIR) {
489 return -0.5 * BS + 0.2;
497 void MapblockMeshGenerator::drawLiquidSides(bool flowing)
499 struct LiquidFaceDesc {
501 v3s16 p[2]; // XZ only; 1 means +, 0 means -
506 static const LiquidFaceDesc base_faces[4] = {
507 {v3s16( 1, 0, 0), {v3s16(1, 0, 1), v3s16(1, 0, 0)}},
508 {v3s16(-1, 0, 0), {v3s16(0, 0, 0), v3s16(0, 0, 1)}},
509 {v3s16( 0, 0, 1), {v3s16(0, 0, 1), v3s16(1, 0, 1)}},
510 {v3s16( 0, 0, -1), {v3s16(1, 0, 0), v3s16(0, 0, 0)}},
512 static const UV base_vertices[4] = {
518 for (int i = 0; i < 4; i++) {
519 const LiquidFaceDesc &face = base_faces[i];
520 const NeighborData &neighbor = liquid_neighbors[face.dir.Z + 1][face.dir.X + 1];
522 // No face between nodes of the same liquid, unless there is node
523 // at the top to which it should be connected. Again, unless the face
524 // there would be inside the liquid
525 if (neighbor.is_same_liquid) {
528 if (!top_is_same_liquid)
530 if (neighbor.top_is_same_liquid)
534 if (!flowing && (neighbor.content == CONTENT_IGNORE))
537 const ContentFeatures &neighbor_features = nodedef->get(neighbor.content);
538 // Don't draw face if neighbor is blocking the view
539 if (neighbor_features.solidness == 2)
542 video::S3DVertex vertices[4];
543 for (int j = 0; j < 4; j++) {
544 const UV &vertex = base_vertices[j];
545 const v3s16 &base = face.p[vertex.u];
547 pos.X = (base.X - 0.5) * BS;
548 pos.Z = (base.Z - 0.5) * BS;
550 pos.Y = neighbor.is_same_liquid ? corner_levels[base.Z][base.X] : -0.5 * BS;
552 pos.Y = !top_is_same_liquid ? corner_levels[base.Z][base.X] : 0.5 * BS;
553 if (data->m_smooth_lighting)
554 color = blendLightColor(pos);
556 vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, color, vertex.u, vertex.v);
558 collector->append(tile_liquid, vertices, 4, quad_indices, 6);
562 void MapblockMeshGenerator::drawLiquidTop(bool flowing)
564 // To get backface culling right, the vertices need to go
565 // clockwise around the front of the face. And we happened to
566 // calculate corner levels in exact reverse order.
567 static const int corner_resolve[4][2] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
569 video::S3DVertex vertices[4] = {
570 video::S3DVertex(-BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 0, 1),
571 video::S3DVertex( BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 1, 1),
572 video::S3DVertex( BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 1, 0),
573 video::S3DVertex(-BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 0, 0),
576 for (int i = 0; i < 4; i++) {
577 int u = corner_resolve[i][0];
578 int w = corner_resolve[i][1];
579 vertices[i].Pos.Y += corner_levels[w][u];
580 if (data->m_smooth_lighting)
581 vertices[i].Color = blendLightColor(vertices[i].Pos);
582 vertices[i].Pos += origin;
586 // Default downwards-flowing texture animation goes from
587 // -Z towards +Z, thus the direction is +Z.
588 // Rotate texture to make animation go in flow direction
589 // Positive if liquid moves towards +Z
590 f32 dz = (corner_levels[0][0] + corner_levels[0][1]) -
591 (corner_levels[1][0] + corner_levels[1][1]);
592 // Positive if liquid moves towards +X
593 f32 dx = (corner_levels[0][0] + corner_levels[1][0]) -
594 (corner_levels[0][1] + corner_levels[1][1]);
595 f32 tcoord_angle = atan2(dz, dx) * core::RADTODEG;
596 v2f tcoord_center(0.5, 0.5);
597 v2f tcoord_translate(blockpos_nodes.Z + p.Z, blockpos_nodes.X + p.X);
598 tcoord_translate.rotateBy(tcoord_angle);
599 tcoord_translate.X -= floor(tcoord_translate.X);
600 tcoord_translate.Y -= floor(tcoord_translate.Y);
602 for (int i = 0; i < 4; i++) {
603 vertices[i].TCoords.rotateBy(tcoord_angle, tcoord_center);
604 vertices[i].TCoords += tcoord_translate;
607 std::swap(vertices[0].TCoords, vertices[2].TCoords);
610 collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
613 void MapblockMeshGenerator::drawLiquidNode(bool flowing)
615 prepareLiquidNodeDrawing(flowing);
616 getLiquidNeighborhood(flowing);
618 calculateCornerLevels();
621 drawLiquidSides(flowing);
622 if (!top_is_same_liquid)
623 drawLiquidTop(flowing);
626 void MapblockMeshGenerator::drawGlasslikeNode()
630 for (int face = 0; face < 6; face++) {
631 // Check this neighbor
632 v3s16 dir = g_6dirs[face];
633 v3s16 neighbor_pos = blockpos_nodes + p + dir;
634 MapNode neighbor = data->m_vmanip.getNodeNoExNoEmerge(neighbor_pos);
635 // Don't make face if neighbor is of same type
636 if (neighbor.getContent() == n.getContent())
640 v3f(-BS / 2, BS / 2, -BS / 2),
641 v3f( BS / 2, BS / 2, -BS / 2),
642 v3f( BS / 2, -BS / 2, -BS / 2),
643 v3f(-BS / 2, -BS / 2, -BS / 2),
645 for (int i = 0; i < 4; i++) {
647 case D6D_ZP: vertices[i].rotateXZBy(180); break;
648 case D6D_YP: vertices[i].rotateYZBy( 90); break;
649 case D6D_XP: vertices[i].rotateXZBy( 90); break;
650 case D6D_ZN: vertices[i].rotateXZBy( 0); break;
651 case D6D_YN: vertices[i].rotateYZBy(-90); break;
652 case D6D_XN: vertices[i].rotateXZBy(-90); break;
655 drawQuad(vertices, dir);
659 void MapblockMeshGenerator::drawGlasslikeFramedNode()
662 for (int face = 0; face < 6; face++)
663 getTile(g_6dirs[face], tiles[face]);
665 TileSpec glass_tiles[6];
666 if (tiles[1].layers[0].texture &&
667 tiles[2].layers[0].texture &&
668 tiles[3].layers[0].texture) {
669 glass_tiles[0] = tiles[4];
670 glass_tiles[1] = tiles[0];
671 glass_tiles[2] = tiles[4];
672 glass_tiles[3] = tiles[4];
673 glass_tiles[4] = tiles[3];
674 glass_tiles[5] = tiles[4];
676 for (int face = 0; face < 6; face++)
677 glass_tiles[face] = tiles[4];
680 u8 param2 = n.getParam2();
681 bool H_merge = !(param2 & 128);
682 bool V_merge = !(param2 & 64);
685 static const float a = BS / 2;
686 static const float g = a - 0.003;
687 static const float b = .876 * ( BS / 2 );
689 static const aabb3f frame_edges[FRAMED_EDGE_COUNT] = {
690 aabb3f( b, b, -a, a, a, a), // y+
691 aabb3f(-a, b, -a, -b, a, a), // y+
692 aabb3f( b, -a, -a, a, -b, a), // y-
693 aabb3f(-a, -a, -a, -b, -b, a), // y-
694 aabb3f( b, -a, b, a, a, a), // x+
695 aabb3f( b, -a, -a, a, a, -b), // x+
696 aabb3f(-a, -a, b, -b, a, a), // x-
697 aabb3f(-a, -a, -a, -b, a, -b), // x-
698 aabb3f(-a, b, b, a, a, a), // z+
699 aabb3f(-a, -a, b, a, -b, a), // z+
700 aabb3f(-a, -a, -a, a, -b, -b), // z-
701 aabb3f(-a, b, -a, a, a, -b), // z-
703 static const aabb3f glass_faces[6] = {
704 aabb3f(-g, -g, g, g, g, g), // z+
705 aabb3f(-g, g, -g, g, g, g), // y+
706 aabb3f( g, -g, -g, g, g, g), // x+
707 aabb3f(-g, -g, -g, g, g, -g), // z-
708 aabb3f(-g, -g, -g, g, -g, g), // y-
709 aabb3f(-g, -g, -g, -g, g, g), // x-
712 // tables of neighbour (connect if same type and merge allowed),
713 // checked with g_26dirs
715 // 1 = connect, 0 = face visible
716 bool nb[FRAMED_NEIGHBOR_COUNT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
719 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};
720 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};
721 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};
722 const bool *check_nb = check_nb_all;
724 // neighbours checks for frames visibility
725 if (H_merge || V_merge) {
727 check_nb = check_nb_vertical; // vertical-only merge
729 check_nb = check_nb_horizontal; // horizontal-only merge
730 content_t current = n.getContent();
731 for (int i = 0; i < FRAMED_NEIGHBOR_COUNT; i++) {
734 v3s16 n2p = blockpos_nodes + p + g_26dirs[i];
735 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
736 content_t n2c = n2.getContent();
737 if (n2c == current || n2c == CONTENT_IGNORE)
744 static const u8 nb_triplet[FRAMED_EDGE_COUNT][3] = {
745 {1, 2, 7}, {1, 5, 6}, {4, 2, 15}, {4, 5, 14},
746 {2, 0, 11}, {2, 3, 13}, {5, 0, 10}, {5, 3, 12},
747 {0, 1, 8}, {0, 4, 16}, {3, 4, 17}, {3, 1, 9},
751 for (int edge = 0; edge < FRAMED_EDGE_COUNT; edge++) {
753 if (nb[nb_triplet[edge][2]])
754 edge_invisible = nb[nb_triplet[edge][0]] & nb[nb_triplet[edge][1]];
756 edge_invisible = nb[nb_triplet[edge][0]] ^ nb[nb_triplet[edge][1]];
759 drawAutoLightedCuboid(frame_edges[edge]);
762 for (int face = 0; face < 6; face++) {
765 tile = glass_tiles[face];
766 drawAutoLightedCuboid(glass_faces[face]);
769 // Optionally render internal liquid level defined by param2
770 // Liquid is textured with 1 tile defined in nodedef 'special_tiles'
771 if (param2 > 0 && f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL &&
772 f->special_tiles[0].layers[0].texture) {
773 // Internal liquid level has param2 range 0 .. 63,
774 // convert it to -0.5 .. 0.5
775 float vlev = (param2 / 63.0) * 2.0 - 1.0;
776 tile = getSpecialTile(*f, n, 0);
777 drawAutoLightedCuboid(aabb3f(-(nb[5] ? g : b),
781 (nb[1] ? g : b) * vlev,
786 void MapblockMeshGenerator::drawAllfacesNode()
788 static const aabb3f box(-BS / 2, -BS / 2, -BS / 2, BS / 2, BS / 2, BS / 2);
789 useDefaultTile(false);
790 drawAutoLightedCuboid(box);
793 void MapblockMeshGenerator::drawTorchlikeNode()
795 u8 wall = n.getWallMounted(nodedef);
798 case DWM_YP: tileindex = 1; break; // ceiling
799 case DWM_YN: tileindex = 0; break; // floor
800 default: tileindex = 2; // side (or invalid—should we care?)
802 useTile(tileindex, true);
804 float size = BS / 2 * f->visual_scale;
808 v3f( size, -size, 0),
809 v3f(-size, -size, 0),
811 for (int i = 0; i < 4; i++) {
813 case DWM_YP: vertices[i].rotateXZBy(-45); break;
814 case DWM_YN: vertices[i].rotateXZBy( 45); break;
815 case DWM_XP: vertices[i].rotateXZBy( 0); break;
816 case DWM_XN: vertices[i].rotateXZBy(180); break;
817 case DWM_ZP: vertices[i].rotateXZBy( 90); break;
818 case DWM_ZN: vertices[i].rotateXZBy(-90); break;
824 void MapblockMeshGenerator::drawSignlikeNode()
826 u8 wall = n.getWallMounted(nodedef);
828 static const float offset = BS / 16;
829 float size = BS / 2 * f->visual_scale;
830 // Wall at X+ of node
832 v3f(BS / 2 - offset, size, size),
833 v3f(BS / 2 - offset, size, -size),
834 v3f(BS / 2 - offset, -size, -size),
835 v3f(BS / 2 - offset, -size, size),
837 for (int i = 0; i < 4; i++) {
839 case DWM_YP: vertices[i].rotateXYBy( 90); break;
840 case DWM_YN: vertices[i].rotateXYBy(-90); break;
841 case DWM_XP: vertices[i].rotateXZBy( 0); break;
842 case DWM_XN: vertices[i].rotateXZBy(180); break;
843 case DWM_ZP: vertices[i].rotateXZBy( 90); break;
844 case DWM_ZN: vertices[i].rotateXZBy(-90); break;
850 void MapblockMeshGenerator::drawPlantlikeQuad(float rotation, float quad_offset,
851 bool offset_top_only)
854 v3f(-scale, -BS / 2 + scale * 2, 0),
855 v3f( scale, -BS / 2 + scale * 2, 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;
866 for (int i = 0; i < 4; i++) {
867 vertices[i].rotateXZBy(rotation + rotate_degree);
868 vertices[i] += offset;
873 void MapblockMeshGenerator::drawPlantlikeNode()
876 draw_style = PLANT_STYLE_CROSS;
877 scale = BS / 2 * f->visual_scale;
878 offset = v3f(0, 0, 0);
880 random_offset_Y = false;
883 switch (f->param_type_2) {
884 case CPT2_MESHOPTIONS:
885 draw_style = PlantlikeStyle(n.param2 & MO_MASK_STYLE);
886 if (n.param2 & MO_BIT_SCALE_SQRT2)
888 if (n.param2 & MO_BIT_RANDOM_OFFSET) {
889 PseudoRandom rng(p.X << 8 | p.Z | p.Y << 16);
890 offset.X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
891 offset.Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
893 if (n.param2 & MO_BIT_RANDOM_OFFSET_Y)
894 random_offset_Y = true;
898 rotate_degree = n.param2 * 2;
905 switch (draw_style) {
906 case PLANT_STYLE_CROSS:
907 drawPlantlikeQuad(46);
908 drawPlantlikeQuad(-44);
911 case PLANT_STYLE_CROSS2:
912 drawPlantlikeQuad(91);
913 drawPlantlikeQuad(1);
916 case PLANT_STYLE_STAR:
917 drawPlantlikeQuad(121);
918 drawPlantlikeQuad(241);
919 drawPlantlikeQuad(1);
922 case PLANT_STYLE_HASH:
923 drawPlantlikeQuad( 1, BS / 4);
924 drawPlantlikeQuad( 91, BS / 4);
925 drawPlantlikeQuad(181, BS / 4);
926 drawPlantlikeQuad(271, BS / 4);
929 case PLANT_STYLE_HASH2:
930 drawPlantlikeQuad( 1, -BS / 2, true);
931 drawPlantlikeQuad( 91, -BS / 2, true);
932 drawPlantlikeQuad(181, -BS / 2, true);
933 drawPlantlikeQuad(271, -BS / 2, true);
938 void MapblockMeshGenerator::drawFirelikeQuad(float rotation, float opening_angle,
939 float offset_h, float offset_v)
942 v3f(-scale, -BS / 2 + scale * 2, 0),
943 v3f( scale, -BS / 2 + scale * 2, 0),
944 v3f( scale, -BS / 2, 0),
945 v3f(-scale, -BS / 2, 0),
947 for (int i = 0; i < 4; i++) {
948 vertices[i].rotateYZBy(opening_angle);
949 vertices[i].Z += offset_h;
950 vertices[i].rotateXZBy(rotation);
951 vertices[i].Y += offset_v;
956 void MapblockMeshGenerator::drawFirelikeNode()
959 scale = BS / 2 * f->visual_scale;
961 // Check for adjacent nodes
962 bool neighbors = false;
963 bool neighbor[6] = {0, 0, 0, 0, 0, 0};
964 content_t current = n.getContent();
965 for (int i = 0; i < 6; i++) {
966 v3s16 n2p = blockpos_nodes + p + g_6dirs[i];
967 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
968 content_t n2c = n2.getContent();
969 if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
974 bool drawBasicFire = neighbor[D6D_YN] || !neighbors;
975 bool drawBottomFire = neighbor[D6D_YP];
977 if (drawBasicFire || neighbor[D6D_ZP])
978 drawFirelikeQuad(0, -10, 0.4 * BS);
979 else if (drawBottomFire)
980 drawFirelikeQuad(0, 70, 0.47 * BS, 0.484 * BS);
982 if (drawBasicFire || neighbor[D6D_XN])
983 drawFirelikeQuad(90, -10, 0.4 * BS);
984 else if (drawBottomFire)
985 drawFirelikeQuad(90, 70, 0.47 * BS, 0.484 * BS);
987 if (drawBasicFire || neighbor[D6D_ZN])
988 drawFirelikeQuad(180, -10, 0.4 * BS);
989 else if (drawBottomFire)
990 drawFirelikeQuad(180, 70, 0.47 * BS, 0.484 * BS);
992 if (drawBasicFire || neighbor[D6D_XP])
993 drawFirelikeQuad(270, -10, 0.4 * BS);
994 else if (drawBottomFire)
995 drawFirelikeQuad(270, 70, 0.47 * BS, 0.484 * BS);
998 drawFirelikeQuad(45, 0, 0.0);
999 drawFirelikeQuad(-45, 0, 0.0);
1003 void MapblockMeshGenerator::drawFencelikeNode()
1005 useDefaultTile(false);
1006 TileSpec tile_nocrack = tile;
1007 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++)
1008 tile_nocrack.layers[layer].material_flags &= ~MATERIAL_FLAG_CRACK;
1010 // Put wood the right way around in the posts
1011 TileSpec tile_rot = tile;
1012 tile_rot.rotation = 1;
1014 static const f32 post_rad = BS / 8;
1015 static const f32 bar_rad = BS / 16;
1016 static const f32 bar_len = BS / 2 - post_rad;
1018 // The post - always present
1019 static const aabb3f post(-post_rad, -BS / 2, -post_rad,
1020 post_rad, BS / 2, post_rad);
1021 static const f32 postuv[24] = {
1022 0.375, 0.375, 0.625, 0.625,
1023 0.375, 0.375, 0.625, 0.625,
1024 0.000, 0.000, 0.250, 1.000,
1025 0.250, 0.000, 0.500, 1.000,
1026 0.500, 0.000, 0.750, 1.000,
1027 0.750, 0.000, 1.000, 1.000,
1030 drawAutoLightedCuboid(post, postuv);
1032 tile = tile_nocrack;
1034 // Now a section of fence, +X, if there's a post there
1037 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1038 const ContentFeatures *f2 = &nodedef->get(n2);
1039 if (f2->drawtype == NDT_FENCELIKE) {
1040 static const aabb3f bar_x1(BS / 2 - bar_len, BS / 4 - bar_rad, -bar_rad,
1041 BS / 2 + bar_len, BS / 4 + bar_rad, bar_rad);
1042 static const aabb3f bar_x2(BS / 2 - bar_len, -BS / 4 - bar_rad, -bar_rad,
1043 BS / 2 + bar_len, -BS / 4 + bar_rad, bar_rad);
1044 static const f32 xrailuv[24] = {
1045 0.000, 0.125, 1.000, 0.250,
1046 0.000, 0.250, 1.000, 0.375,
1047 0.375, 0.375, 0.500, 0.500,
1048 0.625, 0.625, 0.750, 0.750,
1049 0.000, 0.500, 1.000, 0.625,
1050 0.000, 0.875, 1.000, 1.000,
1052 drawAutoLightedCuboid(bar_x1, xrailuv);
1053 drawAutoLightedCuboid(bar_x2, xrailuv);
1056 // Now a section of fence, +Z, if there's a post there
1059 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1060 f2 = &nodedef->get(n2);
1061 if (f2->drawtype == NDT_FENCELIKE) {
1062 static const aabb3f bar_z1(-bar_rad, BS / 4 - bar_rad, BS / 2 - bar_len,
1063 bar_rad, BS / 4 + bar_rad, BS / 2 + bar_len);
1064 static const aabb3f bar_z2(-bar_rad, -BS / 4 - bar_rad, BS / 2 - bar_len,
1065 bar_rad, -BS / 4 + bar_rad, BS / 2 + bar_len);
1066 static const f32 zrailuv[24] = {
1067 0.1875, 0.0625, 0.3125, 0.3125, // cannot rotate; stretch
1068 0.2500, 0.0625, 0.3750, 0.3125, // for wood texture instead
1069 0.0000, 0.5625, 1.0000, 0.6875,
1070 0.0000, 0.3750, 1.0000, 0.5000,
1071 0.3750, 0.3750, 0.5000, 0.5000,
1072 0.6250, 0.6250, 0.7500, 0.7500,
1074 drawAutoLightedCuboid(bar_z1, zrailuv);
1075 drawAutoLightedCuboid(bar_z2, zrailuv);
1079 bool MapblockMeshGenerator::isSameRail(v3s16 dir)
1081 MapNode node2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
1082 if (node2.getContent() == n.getContent())
1084 const ContentFeatures &def2 = nodedef->get(node2);
1085 return ((def2.drawtype == NDT_RAILLIKE) &&
1086 (def2.getGroup(raillike_groupname) == raillike_group));
1089 void MapblockMeshGenerator::drawRaillikeNode()
1091 static const v3s16 direction[4] = {
1097 static const int slope_angle[4] = {0, 180, 90, -90};
1109 static const RailDesc rail_kinds[16] = {
1112 {straight, 0}, // . . . .
1113 {straight, 0}, // . . . +Z
1114 {straight, 0}, // . . -Z .
1115 {straight, 0}, // . . -Z +Z
1116 {straight, 90}, // . -X . .
1117 { curved, 180}, // . -X . +Z
1118 { curved, 270}, // . -X -Z .
1119 {junction, 180}, // . -X -Z +Z
1120 {straight, 90}, // +X . . .
1121 { curved, 90}, // +X . . +Z
1122 { curved, 0}, // +X . -Z .
1123 {junction, 0}, // +X . -Z +Z
1124 {straight, 90}, // +X -X . .
1125 {junction, 90}, // +X -X . +Z
1126 {junction, 270}, // +X -X -Z .
1127 { cross, 0}, // +X -X -Z +Z
1130 raillike_group = nodedef->get(n).getGroup(raillike_groupname);
1135 bool sloped = false;
1136 for (int dir = 0; dir < 4; dir++) {
1137 bool rail_above = isSameRail(direction[dir] + v3s16(0, 1, 0));
1140 angle = slope_angle[dir];
1143 isSameRail(direction[dir]) ||
1144 isSameRail(direction[dir] + v3s16(0, -1, 0)))
1149 tile_index = straight;
1151 tile_index = rail_kinds[code].tile_index;
1152 angle = rail_kinds[code].angle;
1155 useTile(tile_index, true);
1157 static const float offset = BS / 64;
1158 static const float size = BS / 2;
1159 float y2 = sloped ? size : -size;
1161 v3f(-size, y2 + offset, size),
1162 v3f( size, y2 + offset, size),
1163 v3f( size, -size + offset, -size),
1164 v3f(-size, -size + offset, -size),
1167 for (int i = 0; i < 4; i++)
1168 vertices[i].rotateXZBy(angle);
1172 void MapblockMeshGenerator::drawNodeboxNode()
1174 static const v3s16 tile_dirs[6] = {
1183 // we have this order for some reason...
1184 static const v3s16 connection_dirs[6] = {
1185 v3s16( 0, 1, 0), // top
1186 v3s16( 0, -1, 0), // bottom
1187 v3s16( 0, 0, -1), // front
1188 v3s16(-1, 0, 0), // left
1189 v3s16( 0, 0, 1), // back
1190 v3s16( 1, 0, 0), // right
1194 for (int face = 0; face < 6; face++) {
1195 // Handles facedir rotation for textures
1196 getTile(tile_dirs[face], tiles[face]);
1199 // locate possible neighboring nodes to connect to
1200 int neighbors_set = 0;
1201 if (f->node_box.type == NODEBOX_CONNECTED) {
1202 for (int dir = 0; dir != 6; dir++) {
1203 int flag = 1 << dir;
1204 v3s16 p2 = blockpos_nodes + p + connection_dirs[dir];
1205 MapNode n2 = data->m_vmanip.getNodeNoEx(p2);
1206 if (nodedef->nodeboxConnects(n, n2, flag))
1207 neighbors_set |= flag;
1211 std::vector<aabb3f> boxes;
1212 n.getNodeBoxes(nodedef, &boxes, neighbors_set);
1213 for (std::vector<aabb3f>::iterator i = boxes.begin(); i != boxes.end(); ++i)
1214 drawAutoLightedCuboid(*i, NULL, tiles, 6);
1217 void MapblockMeshGenerator::drawMeshNode()
1221 bool private_mesh; // as a grab/drop pair is not thread-safe
1223 if (f->param_type_2 == CPT2_FACEDIR ||
1224 f->param_type_2 == CPT2_COLORED_FACEDIR) {
1225 facedir = n.getFaceDir(nodedef);
1226 } else if (f->param_type_2 == CPT2_WALLMOUNTED ||
1227 f->param_type_2 == CPT2_COLORED_WALLMOUNTED) {
1228 // Convert wallmounted to 6dfacedir.
1229 // When cache enabled, it is already converted.
1230 facedir = n.getWallMounted(nodedef);
1231 if (!enable_mesh_cache) {
1232 static const u8 wm_to_6d[6] = {20, 0, 16 + 1, 12 + 3, 8, 4 + 2};
1233 facedir = wm_to_6d[facedir];
1237 if (!data->m_smooth_lighting && f->mesh_ptr[facedir]) {
1238 // use cached meshes
1239 private_mesh = false;
1240 mesh = f->mesh_ptr[facedir];
1241 } else if (f->mesh_ptr[0]) {
1242 // no cache, clone and rotate mesh
1243 private_mesh = true;
1244 mesh = cloneMesh(f->mesh_ptr[0]);
1245 rotateMeshBy6dFacedir(mesh, facedir);
1246 recalculateBoundingBox(mesh);
1247 meshmanip->recalculateNormals(mesh, true, false);
1251 int mesh_buffer_count = mesh->getMeshBufferCount();
1252 for (int j = 0; j < mesh_buffer_count; j++) {
1254 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1255 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1256 int vertex_count = buf->getVertexCount();
1258 if (data->m_smooth_lighting) {
1259 // Mesh is always private here. So the lighting is applied to each
1260 // vertex right here.
1261 for (int k = 0; k < vertex_count; k++) {
1262 video::S3DVertex &vertex = vertices[k];
1263 vertex.Color = blendLightColor(vertex.Pos, vertex.Normal);
1264 vertex.Pos += origin;
1266 collector->append(tile, vertices, vertex_count,
1267 buf->getIndices(), buf->getIndexCount());
1269 // Don't modify the mesh, it may not be private here.
1270 // Instead, let the collector process colors, etc.
1271 collector->append(tile, vertices, vertex_count,
1272 buf->getIndices(), buf->getIndexCount(), origin,
1273 color, f->light_source);
1280 // also called when the drawtype is known but should have been pre-converted
1281 void MapblockMeshGenerator::errorUnknownDrawtype()
1283 infostream << "Got drawtype " << f->drawtype << std::endl;
1284 FATAL_ERROR("Unknown drawtype");
1287 void MapblockMeshGenerator::drawNode()
1289 if (data->m_smooth_lighting)
1290 getSmoothLightFrame();
1292 light = getInteriorLight(n, 1, nodedef);
1293 switch (f->drawtype) {
1294 case NDT_LIQUID: drawLiquidNode(false); break;
1295 case NDT_FLOWINGLIQUID: drawLiquidNode(true); break;
1296 case NDT_GLASSLIKE: drawGlasslikeNode(); break;
1297 case NDT_GLASSLIKE_FRAMED: drawGlasslikeFramedNode(); break;
1298 case NDT_ALLFACES: drawAllfacesNode(); break;
1299 case NDT_TORCHLIKE: drawTorchlikeNode(); break;
1300 case NDT_SIGNLIKE: drawSignlikeNode(); break;
1301 case NDT_PLANTLIKE: drawPlantlikeNode(); break;
1302 case NDT_FIRELIKE: drawFirelikeNode(); break;
1303 case NDT_FENCELIKE: drawFencelikeNode(); break;
1304 case NDT_RAILLIKE: drawRaillikeNode(); break;
1305 case NDT_NODEBOX: drawNodeboxNode(); break;
1306 case NDT_MESH: drawMeshNode(); break;
1307 default: errorUnknownDrawtype(); break;
1312 TODO: Fix alpha blending for special nodes
1313 Currently only the last element rendered is blended correct
1315 void MapblockMeshGenerator::generate()
1317 for (p.Z = 0; p.Z < MAP_BLOCKSIZE; p.Z++)
1318 for (p.Y = 0; p.Y < MAP_BLOCKSIZE; p.Y++)
1319 for (p.X = 0; p.X < MAP_BLOCKSIZE; p.X++) {
1320 n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1321 f = &nodedef->get(n);
1322 // Solid nodes are drawn by MapBlockMesh
1323 if (f->solidness != 0)
1325 if (f->drawtype == NDT_AIRLIKE)
1327 origin = intToFloat(p, BS);