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 tile = getNodeTileN(n, p, index, data);
80 if (!data->m_smooth_lighting)
81 color = encode_light_and_color(light, tile.color, f->light_source);
82 if (disable_backface_culling)
83 tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
84 tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
87 void MapblockMeshGenerator::useDefaultTile(bool set_color)
89 tile = getNodeTile(n, p, v3s16(0, 0, 0), data);
90 if (set_color && !data->m_smooth_lighting)
91 color = encode_light_and_color(light, tile.color, f->light_source);
94 TileSpec MapblockMeshGenerator::getTile(const v3s16& direction)
96 return getNodeTile(n, p, direction, data);
99 void MapblockMeshGenerator::drawQuad(v3f *coords, const v3s16 &normal)
101 static const v2f tcoords[4] = {v2f(0, 1), v2f(1, 1), v2f(1, 0), v2f(0, 0)};
102 video::S3DVertex vertices[4];
103 bool shade_face = !f->light_source && (normal != v3s16(0, 0, 0));
104 v3f normal2(normal.X, normal.Y, normal.Z);
105 for (int j = 0; j < 4; j++) {
106 vertices[j].Pos = coords[j] + origin;
107 vertices[j].Normal = normal2;
108 if (data->m_smooth_lighting)
109 vertices[j].Color = blendLight(coords[j], tile.color);
111 vertices[j].Color = color;
113 applyFacesShading(vertices[j].Color, normal2);
114 vertices[j].TCoords = tcoords[j];
116 collector->append(tile, vertices, 4, quad_indices, 6);
120 // tiles - the tiles (materials) to use (for all 6 faces)
121 // tilecount - number of entries in tiles, 1<=tilecount<=6
122 // lights - vertex light levels. The order is the same as in light_dirs.
123 // NULL may be passed if smooth lighting is disabled.
124 // txc - texture coordinates - this is a list of texture coordinates
125 // for the opposite corners of each face - therefore, there
126 // should be (2+2)*6=24 values in the list. The order of
127 // the faces in the list is up-down-right-left-back-front
128 // (compatible with ContentFeatures).
129 void MapblockMeshGenerator::drawCuboid(const aabb3f &box,
130 TileSpec *tiles, int tilecount, const u16 *lights, const f32 *txc)
132 assert(tilecount >= 1 && tilecount <= 6); // pre-condition
134 v3f min = box.MinEdge;
135 v3f max = box.MaxEdge;
137 video::SColor colors[6];
138 if (!data->m_smooth_lighting) {
139 for (int face = 0; face != 6; ++face) {
140 int tileindex = MYMIN(face, tilecount - 1);
141 colors[face] = encode_light_and_color(light, tiles[tileindex].color, f->light_source);
143 if (!f->light_source) {
144 applyFacesShading(colors[0], v3f(0, 1, 0));
145 applyFacesShading(colors[1], v3f(0, -1, 0));
146 applyFacesShading(colors[2], v3f(1, 0, 0));
147 applyFacesShading(colors[3], v3f(-1, 0, 0));
148 applyFacesShading(colors[4], v3f(0, 0, 1));
149 applyFacesShading(colors[5], v3f(0, 0, -1));
153 video::S3DVertex vertices[24] = {
155 video::S3DVertex(min.X, max.Y, max.Z, 0, 1, 0, colors[0], txc[0], txc[1]),
156 video::S3DVertex(max.X, max.Y, max.Z, 0, 1, 0, colors[0], txc[2], txc[1]),
157 video::S3DVertex(max.X, max.Y, min.Z, 0, 1, 0, colors[0], txc[2], txc[3]),
158 video::S3DVertex(min.X, max.Y, min.Z, 0, 1, 0, colors[0], txc[0], txc[3]),
160 video::S3DVertex(min.X, min.Y, min.Z, 0, -1, 0, colors[1], txc[4], txc[5]),
161 video::S3DVertex(max.X, min.Y, min.Z, 0, -1, 0, colors[1], txc[6], txc[5]),
162 video::S3DVertex(max.X, min.Y, max.Z, 0, -1, 0, colors[1], txc[6], txc[7]),
163 video::S3DVertex(min.X, min.Y, max.Z, 0, -1, 0, colors[1], txc[4], txc[7]),
165 video::S3DVertex(max.X, max.Y, min.Z, 1, 0, 0, colors[2], txc[ 8], txc[9]),
166 video::S3DVertex(max.X, max.Y, max.Z, 1, 0, 0, colors[2], txc[10], txc[9]),
167 video::S3DVertex(max.X, min.Y, max.Z, 1, 0, 0, colors[2], txc[10], txc[11]),
168 video::S3DVertex(max.X, min.Y, min.Z, 1, 0, 0, colors[2], txc[ 8], txc[11]),
170 video::S3DVertex(min.X, max.Y, max.Z, -1, 0, 0, colors[3], txc[12], txc[13]),
171 video::S3DVertex(min.X, max.Y, min.Z, -1, 0, 0, colors[3], txc[14], txc[13]),
172 video::S3DVertex(min.X, min.Y, min.Z, -1, 0, 0, colors[3], txc[14], txc[15]),
173 video::S3DVertex(min.X, min.Y, max.Z, -1, 0, 0, colors[3], txc[12], txc[15]),
175 video::S3DVertex(max.X, max.Y, max.Z, 0, 0, 1, colors[4], txc[16], txc[17]),
176 video::S3DVertex(min.X, max.Y, max.Z, 0, 0, 1, colors[4], txc[18], txc[17]),
177 video::S3DVertex(min.X, min.Y, max.Z, 0, 0, 1, colors[4], txc[18], txc[19]),
178 video::S3DVertex(max.X, min.Y, max.Z, 0, 0, 1, colors[4], txc[16], txc[19]),
180 video::S3DVertex(min.X, max.Y, min.Z, 0, 0, -1, colors[5], txc[20], txc[21]),
181 video::S3DVertex(max.X, max.Y, min.Z, 0, 0, -1, colors[5], txc[22], txc[21]),
182 video::S3DVertex(max.X, min.Y, min.Z, 0, 0, -1, colors[5], txc[22], txc[23]),
183 video::S3DVertex(min.X, min.Y, min.Z, 0, 0, -1, colors[5], txc[20], txc[23]),
186 static const u8 light_indices[24] = {
195 for (int face = 0; face < 6; face++) {
196 int tileindex = MYMIN(face, tilecount - 1);
197 const TileSpec &tile = tiles[tileindex];
198 for (int j = 0; j < 4; j++) {
199 video::S3DVertex &vertex = vertices[face * 4 + j];
200 v2f &tcoords = vertex.TCoords;
201 switch (tile.rotation) {
205 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
208 tcoords.rotateBy(180, irr::core::vector2df(0, 0));
211 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
214 tcoords.X = 1.0 - tcoords.X;
215 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
218 tcoords.X = 1.0 - tcoords.X;
219 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
222 tcoords.Y = 1.0 - tcoords.Y;
223 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
226 tcoords.Y = 1.0 - tcoords.Y;
227 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
230 tcoords.X = 1.0 - tcoords.X;
233 tcoords.Y = 1.0 - tcoords.Y;
241 if (data->m_smooth_lighting) {
242 for (int j = 0; j < 24; ++j) {
243 int tileindex = MYMIN(j / 4, tilecount - 1);
244 vertices[j].Color = encode_light_and_color(lights[light_indices[j]],
245 tiles[tileindex].color, f->light_source);
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::blendLight(const v3f &vertex_pos,
293 video::SColor tile_color)
295 u16 light = blendLight(vertex_pos);
296 return encode_light_and_color(light, tile_color, f->light_source);
299 video::SColor MapblockMeshGenerator::blendLight(const v3f &vertex_pos,
300 const v3f &vertex_normal, video::SColor tile_color)
302 video::SColor color = blendLight(vertex_pos, tile_color);
303 if (!f->light_source)
304 applyFacesShading(color, vertex_normal);
308 void MapblockMeshGenerator::generateCuboidTextureCoords(const aabb3f &box, f32 *coords)
310 f32 tx1 = (box.MinEdge.X / BS) + 0.5;
311 f32 ty1 = (box.MinEdge.Y / BS) + 0.5;
312 f32 tz1 = (box.MinEdge.Z / BS) + 0.5;
313 f32 tx2 = (box.MaxEdge.X / BS) + 0.5;
314 f32 ty2 = (box.MaxEdge.Y / BS) + 0.5;
315 f32 tz2 = (box.MaxEdge.Z / BS) + 0.5;
317 tx1, 1 - tz2, tx2, 1 - tz1, // up
318 tx1, tz1, tx2, tz2, // down
319 tz1, 1 - ty2, tz2, 1 - ty1, // right
320 1 - tz2, 1 - ty2, 1 - tz1, 1 - ty1, // left
321 1 - tx2, 1 - ty2, 1 - tx1, 1 - ty1, // back
322 tx1, 1 - ty2, tx2, 1 - ty1, // front
324 for (int i = 0; i != 24; ++i)
328 void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc,
329 TileSpec *tiles, int tile_count)
331 f32 texture_coord_buf[24];
332 f32 dx1 = box.MinEdge.X;
333 f32 dy1 = box.MinEdge.Y;
334 f32 dz1 = box.MinEdge.Z;
335 f32 dx2 = box.MaxEdge.X;
336 f32 dy2 = box.MaxEdge.Y;
337 f32 dz2 = box.MaxEdge.Z;
338 box.MinEdge += origin;
339 box.MaxEdge += origin;
341 generateCuboidTextureCoords(box, texture_coord_buf);
342 txc = texture_coord_buf;
348 if (data->m_smooth_lighting) {
350 for (int j = 0; j < 8; ++j) {
352 d.X = (j & 4) ? dx2 : dx1;
353 d.Y = (j & 2) ? dy2 : dy1;
354 d.Z = (j & 1) ? dz2 : dz1;
355 lights[j] = blendLight(d);
357 drawCuboid(box, tiles, tile_count, lights, txc);
359 drawCuboid(box, tiles, tile_count, NULL, txc);
364 * Returns the i-th special tile for a map node.
366 static TileSpec getSpecialTile(const ContentFeatures &f,
367 const MapNode &n, u8 i)
369 TileSpec copy = f.special_tiles[i];
371 n.getColor(f, ©.color);
375 void MapblockMeshGenerator::prepareLiquidNodeDrawing(bool flowing)
377 tile_liquid_top = getSpecialTile(*f, n, 0);
378 tile_liquid = getSpecialTile(*f, n, flowing ? 1 : 0);
380 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y + 1, p.Z));
381 c_flowing = nodedef->getId(f->liquid_alternative_flowing);
382 c_source = nodedef->getId(f->liquid_alternative_source);
383 top_is_same_liquid = (ntop.getContent() == c_flowing) || (ntop.getContent() == c_source);
385 if (data->m_smooth_lighting)
386 return; // don't need to pre-compute anything in this case
388 if (f->light_source != 0) {
389 // If this liquid emits light and doesn't contain light, draw
390 // it at what it emits, for an increased effect
391 light = decode_light(f->light_source);
392 light = light | (light << 8);
393 } else if (nodedef->get(ntop).param_type == CPT_LIGHT) {
394 // Otherwise, use the light of the node on top if possible
395 light = getInteriorLight(ntop, 0, nodedef);
398 color_liquid_top = encode_light_and_color(light, tile_liquid_top.color, f->light_source);
399 color = encode_light_and_color(light, tile_liquid.color, f->light_source);
402 void MapblockMeshGenerator::getLiquidNeighborhood(bool flowing)
404 u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8);
406 for (int w = -1; w <= 1; w++)
407 for (int u = -1; u <= 1; u++) {
408 // Skip getting unneeded data
409 if (!flowing && u && w)
412 NeighborData &neighbor = liquid_neighbors[w + 1][u + 1];
413 v3s16 p2 = p + v3s16(u, 0, w);
414 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
415 neighbor.content = n2.getContent();
416 neighbor.level = -0.5 * BS;
417 neighbor.is_same_liquid = false;
418 neighbor.top_is_same_liquid = false;
420 if (neighbor.content == CONTENT_IGNORE)
423 if (neighbor.content == c_source) {
424 neighbor.is_same_liquid = true;
425 neighbor.level = 0.5 * BS;
426 } else if (neighbor.content == c_flowing) {
427 neighbor.is_same_liquid = true;
428 u8 liquid_level = (n2.param2 & LIQUID_LEVEL_MASK);
429 if (liquid_level <= LIQUID_LEVEL_MAX + 1 - range)
432 liquid_level -= (LIQUID_LEVEL_MAX + 1 - range);
433 neighbor.level = (-0.5 + (liquid_level + 0.5) / range) * BS;
436 // Check node above neighbor.
437 // NOTE: This doesn't get executed if neighbor
440 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
441 if (n2.getContent() == c_source || n2.getContent() == c_flowing)
442 neighbor.top_is_same_liquid = true;
446 void MapblockMeshGenerator::resetCornerLevels()
448 for (int k = 0; k < 2; k++)
449 for (int i = 0; i < 2; i++)
450 corner_levels[k][i] = 0.5 * BS;
453 void MapblockMeshGenerator::calculateCornerLevels()
455 for (int k = 0; k < 2; k++)
456 for (int i = 0; i < 2; i++)
457 corner_levels[k][i] = getCornerLevel(i, k);
460 f32 MapblockMeshGenerator::getCornerLevel(int i, int k)
465 for (int dk = 0; dk < 2; dk++)
466 for (int di = 0; di < 2; di++) {
467 NeighborData &neighbor_data = liquid_neighbors[k + dk][i + di];
468 content_t content = neighbor_data.content;
470 // If top is liquid, draw starting from top of node
471 if (neighbor_data.top_is_same_liquid)
474 // Source always has the full height
475 if (content == c_source)
478 // Flowing liquid has level information
479 if (content == c_flowing) {
480 sum += neighbor_data.level;
482 } else if (content == CONTENT_AIR) {
485 return -0.5 * BS + 0.2;
493 void MapblockMeshGenerator::drawLiquidSides(bool flowing)
495 struct LiquidFaceDesc {
497 v3s16 p[2]; // XZ only; 1 means +, 0 means -
502 static const LiquidFaceDesc base_faces[4] = {
503 {v3s16( 1, 0, 0), {v3s16(1, 0, 1), v3s16(1, 0, 0)}},
504 {v3s16(-1, 0, 0), {v3s16(0, 0, 0), v3s16(0, 0, 1)}},
505 {v3s16( 0, 0, 1), {v3s16(0, 0, 1), v3s16(1, 0, 1)}},
506 {v3s16( 0, 0, -1), {v3s16(1, 0, 0), v3s16(0, 0, 0)}},
508 static const UV base_vertices[4] = {
514 for (int i = 0; i < 4; i++) {
515 const LiquidFaceDesc &face = base_faces[i];
516 const NeighborData &neighbor = liquid_neighbors[face.dir.Z + 1][face.dir.X + 1];
518 // No face between nodes of the same liquid, unless there is node
519 // at the top to which it should be connected. Again, unless the face
520 // there would be inside the liquid
521 if (neighbor.is_same_liquid) {
524 if (!top_is_same_liquid)
526 if (neighbor.top_is_same_liquid)
530 if (!flowing && (neighbor.content == CONTENT_IGNORE))
533 const ContentFeatures &neighbor_features = nodedef->get(neighbor.content);
534 // Don't draw face if neighbor is blocking the view
535 if (neighbor_features.solidness == 2)
538 video::S3DVertex vertices[4];
539 for (int j = 0; j < 4; j++) {
540 const UV &vertex = base_vertices[j];
541 const v3s16 &base = face.p[vertex.u];
543 pos.X = (base.X - 0.5) * BS;
544 pos.Z = (base.Z - 0.5) * BS;
546 pos.Y = neighbor.is_same_liquid ? corner_levels[base.Z][base.X] : -0.5 * BS;
548 pos.Y = !top_is_same_liquid ? corner_levels[base.Z][base.X] : 0.5 * BS;
549 if (data->m_smooth_lighting)
550 color = blendLight(pos, tile_liquid.color);
552 vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, color, vertex.u, vertex.v);
554 collector->append(tile_liquid, vertices, 4, quad_indices, 6);
558 void MapblockMeshGenerator::drawLiquidTop(bool flowing)
560 // To get backface culling right, the vertices need to go
561 // clockwise around the front of the face. And we happened to
562 // calculate corner levels in exact reverse order.
563 static const int corner_resolve[4][2] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
565 video::S3DVertex vertices[4] = {
566 video::S3DVertex(-BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 0, 1),
567 video::S3DVertex( BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 1, 1),
568 video::S3DVertex( BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 1, 0),
569 video::S3DVertex(-BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 0, 0),
572 for (int i = 0; i < 4; i++) {
573 int u = corner_resolve[i][0];
574 int w = corner_resolve[i][1];
575 vertices[i].Pos.Y += corner_levels[w][u];
576 if (data->m_smooth_lighting)
577 vertices[i].Color = blendLight(vertices[i].Pos, tile_liquid_top.color);
578 vertices[i].Pos += origin;
582 // Default downwards-flowing texture animation goes from
583 // -Z towards +Z, thus the direction is +Z.
584 // Rotate texture to make animation go in flow direction
585 // Positive if liquid moves towards +Z
586 f32 dz = (corner_levels[0][0] + corner_levels[0][1]) -
587 (corner_levels[1][0] + corner_levels[1][1]);
588 // Positive if liquid moves towards +X
589 f32 dx = (corner_levels[0][0] + corner_levels[1][0]) -
590 (corner_levels[0][1] + corner_levels[1][1]);
591 f32 tcoord_angle = atan2(dz, dx) * core::RADTODEG;
592 v2f tcoord_center(0.5, 0.5);
593 v2f tcoord_translate(blockpos_nodes.Z + p.Z, blockpos_nodes.X + p.X);
594 tcoord_translate.rotateBy(tcoord_angle);
595 tcoord_translate.X -= floor(tcoord_translate.X);
596 tcoord_translate.Y -= floor(tcoord_translate.Y);
598 for (int i = 0; i < 4; i++) {
599 vertices[i].TCoords.rotateBy(tcoord_angle, tcoord_center);
600 vertices[i].TCoords += tcoord_translate;
603 std::swap(vertices[0].TCoords, vertices[2].TCoords);
606 collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
609 void MapblockMeshGenerator::drawLiquidNode(bool flowing)
611 prepareLiquidNodeDrawing(flowing);
612 getLiquidNeighborhood(flowing);
614 calculateCornerLevels();
617 drawLiquidSides(flowing);
618 if (!top_is_same_liquid)
619 drawLiquidTop(flowing);
622 void MapblockMeshGenerator::drawGlasslikeNode()
626 for (int face = 0; face < 6; face++) {
627 // Check this neighbor
628 v3s16 dir = g_6dirs[face];
629 v3s16 neighbor_pos = blockpos_nodes + p + dir;
630 MapNode neighbor = data->m_vmanip.getNodeNoExNoEmerge(neighbor_pos);
631 // Don't make face if neighbor is of same type
632 if (neighbor.getContent() == n.getContent())
635 v3f(-BS / 2, -BS / 2, BS / 2),
636 v3f( BS / 2, -BS / 2, BS / 2),
637 v3f( BS / 2, BS / 2, BS / 2),
638 v3f(-BS / 2, BS / 2, BS / 2),
640 for (int i = 0; i < 4; i++) {
641 // Rotations in the g_6dirs format
643 case 0: vertices[i].rotateXZBy( 0); break; // Z+
644 case 1: vertices[i].rotateYZBy(-90); break; // Y+
645 case 2: vertices[i].rotateXZBy(-90); break; // X+
646 case 3: vertices[i].rotateXZBy(180); break; // Z-
647 case 4: vertices[i].rotateYZBy( 90); break; // Y-
648 case 5: vertices[i].rotateXZBy( 90); break; // X-
651 drawQuad(vertices, dir);
655 void MapblockMeshGenerator::drawGlasslikeFramedNode()
658 for (int face = 0; face < 6; face++)
659 tiles[face] = getTile(g_6dirs[face]);
661 TileSpec glass_tiles[6];
662 if (tiles[0].texture && tiles[3].texture && tiles[4].texture) {
663 glass_tiles[0] = tiles[4];
664 glass_tiles[1] = tiles[0];
665 glass_tiles[2] = tiles[4];
666 glass_tiles[3] = tiles[4];
667 glass_tiles[4] = tiles[3];
668 glass_tiles[5] = tiles[4];
670 for (int face = 0; face < 6; face++)
671 glass_tiles[face] = tiles[4];
674 u8 param2 = n.getParam2();
675 bool H_merge = !(param2 & 128);
676 bool V_merge = !(param2 & 64);
679 static const float a = BS / 2;
680 static const float g = a - 0.003;
681 static const float b = .876 * ( BS / 2 );
683 static const aabb3f frame_edges[FRAMED_EDGE_COUNT] = {
684 aabb3f( b, b, -a, a, a, a), // y+
685 aabb3f(-a, b, -a, -b, a, a), // y+
686 aabb3f( b, -a, -a, a, -b, a), // y-
687 aabb3f(-a, -a, -a, -b, -b, a), // y-
688 aabb3f( b, -a, b, a, a, a), // x+
689 aabb3f( b, -a, -a, a, a, -b), // x+
690 aabb3f(-a, -a, b, -b, a, a), // x-
691 aabb3f(-a, -a, -a, -b, a, -b), // x-
692 aabb3f(-a, b, b, a, a, a), // z+
693 aabb3f(-a, -a, b, a, -b, a), // z+
694 aabb3f(-a, -a, -a, a, -b, -b), // z-
695 aabb3f(-a, b, -a, a, a, -b), // z-
697 static const aabb3f glass_faces[6] = {
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+
701 aabb3f(-g, -g, -g, g, g, -g), // z-
702 aabb3f(-g, -g, -g, g, -g, g), // y-
703 aabb3f(-g, -g, -g, -g, g, g), // x-
706 // tables of neighbour (connect if same type and merge allowed),
707 // checked with g_26dirs
709 // 1 = connect, 0 = face visible
710 bool nb[FRAMED_NEIGHBOR_COUNT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
713 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};
714 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};
715 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};
716 const bool *check_nb = check_nb_all;
718 // neighbours checks for frames visibility
719 if (H_merge || V_merge) {
721 check_nb = check_nb_vertical; // vertical-only merge
723 check_nb = check_nb_horizontal; // horizontal-only merge
724 content_t current = n.getContent();
725 for (int i = 0; i < FRAMED_NEIGHBOR_COUNT; i++) {
728 v3s16 n2p = blockpos_nodes + p + g_26dirs[i];
729 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
730 content_t n2c = n2.getContent();
731 if (n2c == current || n2c == CONTENT_IGNORE)
738 static const u8 nb_triplet[FRAMED_EDGE_COUNT][3] = {
739 {1, 2, 7}, {1, 5, 6}, {4, 2, 15}, {4, 5, 14},
740 {2, 0, 11}, {2, 3, 13}, {5, 0, 10}, {5, 3, 12},
741 {0, 1, 8}, {0, 4, 16}, {3, 4, 17}, {3, 1, 9},
745 for (int edge = 0; edge < FRAMED_EDGE_COUNT; edge++) {
747 if (nb[nb_triplet[edge][2]])
748 edge_invisible = nb[nb_triplet[edge][0]] & nb[nb_triplet[edge][1]];
750 edge_invisible = nb[nb_triplet[edge][0]] ^ nb[nb_triplet[edge][1]];
753 drawAutoLightedCuboid(frame_edges[edge]);
756 for (int face = 0; face < 6; face++) {
759 tile = glass_tiles[face];
760 drawAutoLightedCuboid(glass_faces[face]);
763 if (param2 > 0 && f->special_tiles[0].texture) {
764 // Interior volume level is in range 0 .. 63,
765 // convert it to -0.5 .. 0.5
766 float vlev = (param2 / 63.0) * 2.0 - 1.0;
767 tile = getSpecialTile(*f, n, 0);
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);
780 useDefaultTile(false);
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, true);
795 float size = BS / 2 * f->visual_scale;
797 v3f(-size, -size, 0),
798 v3f( size, -size, 0),
802 for (int i = 0; i < 4; i++) {
804 case DWM_YP: vertices[i].rotateXZBy(-45); break;
805 case DWM_YN: vertices[i].rotateXZBy( 45); break;
806 case DWM_XP: vertices[i].rotateXZBy( 0); break;
807 case DWM_XN: vertices[i].rotateXZBy(180); break;
808 case DWM_ZP: vertices[i].rotateXZBy( 90); break;
809 case DWM_ZN: vertices[i].rotateXZBy(-90); break;
815 void MapblockMeshGenerator::drawSignlikeNode()
817 u8 wall = n.getWallMounted(nodedef);
819 static const float offset = BS / 16;
820 float size = BS / 2 * f->visual_scale;
821 // Wall at X+ of node
823 v3f(BS / 2 - offset, size, size),
824 v3f(BS / 2 - offset, size, -size),
825 v3f(BS / 2 - offset, -size, -size),
826 v3f(BS / 2 - offset, -size, size),
828 for (int i = 0; i < 4; i++) {
830 case DWM_YP: vertices[i].rotateXYBy( 90); break;
831 case DWM_YN: vertices[i].rotateXYBy(-90); break;
832 case DWM_XP: vertices[i].rotateXZBy( 0); break;
833 case DWM_XN: vertices[i].rotateXZBy(180); break;
834 case DWM_ZP: vertices[i].rotateXZBy( 90); break;
835 case DWM_ZN: vertices[i].rotateXZBy(-90); break;
841 void MapblockMeshGenerator::drawPlantlikeQuad(float rotation, float quad_offset,
842 bool offset_top_only)
845 v3f(-scale, -BS / 2, 0),
846 v3f( scale, -BS / 2, 0),
847 v3f( scale, -BS / 2 + scale * 2, 0),
848 v3f(-scale, -BS / 2 + scale * 2, 0),
850 if (random_offset_Y) {
851 PseudoRandom yrng(face_num++ | p.X << 16 | p.Z << 8 | p.Y << 24);
852 offset.Y = BS * ((yrng.next() % 16 / 16.0) * 0.125);
854 int offset_first_index = offset_top_only ? 2 : 0;
855 for (int i = 0; i < 4; i++) {
856 if (i >= offset_first_index)
857 vertices[i].Z += quad_offset;
858 vertices[i].rotateXZBy(rotation + rotate_degree);
859 vertices[i] += offset;
864 void MapblockMeshGenerator::drawPlantlikeNode()
867 draw_style = PLANT_STYLE_CROSS;
868 scale = BS / 2 * f->visual_scale;
869 offset = v3f(0, 0, 0);
871 random_offset_Y = false;
874 switch (f->param_type_2) {
875 case CPT2_MESHOPTIONS:
876 draw_style = PlantlikeStyle(n.param2 & MO_MASK_STYLE);
877 if (n.param2 & MO_BIT_SCALE_SQRT2)
879 if (n.param2 & MO_BIT_RANDOM_OFFSET) {
880 PseudoRandom rng(p.X << 8 | p.Z | p.Y << 16);
881 offset.X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
882 offset.Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
884 if (n.param2 & MO_BIT_RANDOM_OFFSET_Y)
885 random_offset_Y = true;
889 rotate_degree = n.param2 * 2;
896 switch (draw_style) {
897 case PLANT_STYLE_CROSS:
898 drawPlantlikeQuad(46);
899 drawPlantlikeQuad(-44);
902 case PLANT_STYLE_CROSS2:
903 drawPlantlikeQuad(91);
904 drawPlantlikeQuad(1);
907 case PLANT_STYLE_STAR:
908 drawPlantlikeQuad(121);
909 drawPlantlikeQuad(241);
910 drawPlantlikeQuad(1);
913 case PLANT_STYLE_HASH:
914 drawPlantlikeQuad( 1, BS / 4);
915 drawPlantlikeQuad( 91, BS / 4);
916 drawPlantlikeQuad(181, BS / 4);
917 drawPlantlikeQuad(271, BS / 4);
920 case PLANT_STYLE_HASH2:
921 drawPlantlikeQuad( 1, -BS / 2, true);
922 drawPlantlikeQuad( 91, -BS / 2, true);
923 drawPlantlikeQuad(181, -BS / 2, true);
924 drawPlantlikeQuad(271, -BS / 2, true);
929 void MapblockMeshGenerator::drawFirelikeQuad(float rotation, float opening_angle,
930 float offset_h, float offset_v)
933 v3f(-scale, -BS / 2, 0),
934 v3f( scale, -BS / 2, 0),
935 v3f( scale, -BS / 2 + scale * 2, 0),
936 v3f(-scale, -BS / 2 + scale * 2, 0),
938 for (int i = 0; i < 4; i++) {
939 vertices[i].rotateYZBy(opening_angle);
940 vertices[i].Z += offset_h;
941 vertices[i].rotateXZBy(rotation);
942 vertices[i].Y += offset_v;
947 void MapblockMeshGenerator::drawFirelikeNode()
950 scale = BS / 2 * f->visual_scale;
952 // Check for adjacent nodes
953 bool neighbors = false;
954 bool neighbor[6] = {0, 0, 0, 0, 0, 0};
955 content_t current = n.getContent();
956 for (int i = 0; i < 6; i++) {
957 v3s16 n2p = blockpos_nodes + p + g_6dirs[i];
958 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
959 content_t n2c = n2.getContent();
960 if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
965 bool drawBasicFire = neighbor[D6D_YN] || !neighbors;
966 bool drawBottomFire = neighbor[D6D_YP];
968 if (drawBasicFire || neighbor[D6D_ZP])
969 drawFirelikeQuad(0, -10, 0.4 * BS);
970 else if (drawBottomFire)
971 drawFirelikeQuad(0, 70, 0.47 * BS, 0.484 * BS);
973 if (drawBasicFire || neighbor[D6D_XN])
974 drawFirelikeQuad(90, -10, 0.4 * BS);
975 else if (drawBottomFire)
976 drawFirelikeQuad(90, 70, 0.47 * BS, 0.484 * BS);
978 if (drawBasicFire || neighbor[D6D_ZN])
979 drawFirelikeQuad(180, -10, 0.4 * BS);
980 else if (drawBottomFire)
981 drawFirelikeQuad(180, 70, 0.47 * BS, 0.484 * BS);
983 if (drawBasicFire || neighbor[D6D_XP])
984 drawFirelikeQuad(270, -10, 0.4 * BS);
985 else if (drawBottomFire)
986 drawFirelikeQuad(270, 70, 0.47 * BS, 0.484 * BS);
989 drawFirelikeQuad(45, 0, 0.0);
990 drawFirelikeQuad(-45, 0, 0.0);
994 void MapblockMeshGenerator::drawFencelikeNode()
996 useDefaultTile(false);
997 TileSpec tile_nocrack = tile;
998 tile_nocrack.material_flags &= ~MATERIAL_FLAG_CRACK;
1000 // Put wood the right way around in the posts
1001 TileSpec tile_rot = tile;
1002 tile_rot.rotation = 1;
1004 static const f32 post_rad = BS / 8;
1005 static const f32 bar_rad = BS / 16;
1006 static const f32 bar_len = BS / 2 - post_rad;
1008 // The post - always present
1009 static const aabb3f post(-post_rad, -BS / 2, -post_rad,
1010 post_rad, BS / 2, post_rad);
1011 static const f32 postuv[24] = {
1012 0.375, 0.375, 0.625, 0.625,
1013 0.375, 0.375, 0.625, 0.625,
1014 0.000, 0.000, 0.250, 1.000,
1015 0.250, 0.000, 0.500, 1.000,
1016 0.500, 0.000, 0.750, 1.000,
1017 0.750, 0.000, 1.000, 1.000,
1020 drawAutoLightedCuboid(post, postuv);
1022 tile = tile_nocrack;
1024 // Now a section of fence, +X, if there's a post there
1027 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1028 const ContentFeatures *f2 = &nodedef->get(n2);
1029 if (f2->drawtype == NDT_FENCELIKE) {
1030 static const aabb3f bar_x1(BS / 2 - bar_len, BS / 4 - bar_rad, -bar_rad,
1031 BS / 2 + bar_len, BS / 4 + bar_rad, bar_rad);
1032 static const aabb3f bar_x2(BS / 2 - bar_len, -BS / 4 - bar_rad, -bar_rad,
1033 BS / 2 + bar_len, -BS / 4 + bar_rad, bar_rad);
1034 static const f32 xrailuv[24] = {
1035 0.000, 0.125, 1.000, 0.250,
1036 0.000, 0.250, 1.000, 0.375,
1037 0.375, 0.375, 0.500, 0.500,
1038 0.625, 0.625, 0.750, 0.750,
1039 0.000, 0.500, 1.000, 0.625,
1040 0.000, 0.875, 1.000, 1.000,
1042 drawAutoLightedCuboid(bar_x1, xrailuv);
1043 drawAutoLightedCuboid(bar_x2, xrailuv);
1046 // Now a section of fence, +Z, if there's a post there
1049 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1050 f2 = &nodedef->get(n2);
1051 if (f2->drawtype == NDT_FENCELIKE) {
1052 static const aabb3f bar_z1(-bar_rad, BS / 4 - bar_rad, BS / 2 - bar_len,
1053 bar_rad, BS / 4 + bar_rad, BS / 2 + bar_len);
1054 static const aabb3f bar_z2(-bar_rad, -BS / 4 - bar_rad, BS / 2 - bar_len,
1055 bar_rad, -BS / 4 + bar_rad, BS / 2 + bar_len);
1056 static const f32 zrailuv[24] = {
1057 0.1875, 0.0625, 0.3125, 0.3125, // cannot rotate; stretch
1058 0.2500, 0.0625, 0.3750, 0.3125, // for wood texture instead
1059 0.0000, 0.5625, 1.0000, 0.6875,
1060 0.0000, 0.3750, 1.0000, 0.5000,
1061 0.3750, 0.3750, 0.5000, 0.5000,
1062 0.6250, 0.6250, 0.7500, 0.7500,
1064 drawAutoLightedCuboid(bar_z1, zrailuv);
1065 drawAutoLightedCuboid(bar_z2, zrailuv);
1069 bool MapblockMeshGenerator::isSameRail(v3s16 dir)
1071 MapNode node2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
1072 if (node2.getContent() == n.getContent())
1074 const ContentFeatures &def2 = nodedef->get(node2);
1075 return ((def2.drawtype == NDT_RAILLIKE) &&
1076 (def2.getGroup(raillike_groupname) == raillike_group));
1079 void MapblockMeshGenerator::drawRaillikeNode()
1081 static const v3s16 direction[4] = {
1087 static const int slope_angle[4] = {0, 180, 90, -90};
1099 static const RailDesc rail_kinds[16] = {
1102 {straight, 0}, // . . . .
1103 {straight, 0}, // . . . +Z
1104 {straight, 0}, // . . -Z .
1105 {straight, 0}, // . . -Z +Z
1106 {straight, 90}, // . -X . .
1107 { curved, 180}, // . -X . +Z
1108 { curved, 270}, // . -X -Z .
1109 {junction, 180}, // . -X -Z +Z
1110 {straight, 90}, // +X . . .
1111 { curved, 90}, // +X . . +Z
1112 { curved, 0}, // +X . -Z .
1113 {junction, 0}, // +X . -Z +Z
1114 {straight, 90}, // +X -X . .
1115 {junction, 90}, // +X -X . +Z
1116 {junction, 270}, // +X -X -Z .
1117 { cross, 0}, // +X -X -Z +Z
1120 raillike_group = nodedef->get(n).getGroup(raillike_groupname);
1125 bool sloped = false;
1126 for (int dir = 0; dir < 4; dir++) {
1127 bool rail_above = isSameRail(direction[dir] + v3s16(0, 1, 0));
1130 angle = slope_angle[dir];
1133 isSameRail(direction[dir]) ||
1134 isSameRail(direction[dir] + v3s16(0, -1, 0)))
1139 tile_index = straight;
1141 tile_index = rail_kinds[code].tile_index;
1142 angle = rail_kinds[code].angle;
1145 useTile(tile_index, true);
1147 static const float offset = BS / 64;
1148 static const float size = BS / 2;
1149 float y2 = sloped ? size : -size;
1151 v3f(-size, -size + offset, -size),
1152 v3f( size, -size + offset, -size),
1153 v3f( size, y2 + offset, size),
1154 v3f(-size, y2 + offset, size),
1157 for (int i = 0; i < 4; i++)
1158 vertices[i].rotateXZBy(angle);
1162 void MapblockMeshGenerator::drawNodeboxNode()
1164 static const v3s16 tile_dirs[6] = {
1173 // we have this order for some reason...
1174 static const v3s16 connection_dirs[6] = {
1175 v3s16( 0, 1, 0), // top
1176 v3s16( 0, -1, 0), // bottom
1177 v3s16( 0, 0, -1), // front
1178 v3s16(-1, 0, 0), // left
1179 v3s16( 0, 0, 1), // back
1180 v3s16( 1, 0, 0), // right
1184 for (int face = 0; face < 6; face++) {
1185 // Handles facedir rotation for textures
1186 tiles[face] = getTile(tile_dirs[face]);
1189 // locate possible neighboring nodes to connect to
1190 int neighbors_set = 0;
1191 if (f->node_box.type == NODEBOX_CONNECTED) {
1192 for (int dir = 0; dir != 6; dir++) {
1193 int flag = 1 << dir;
1194 v3s16 p2 = blockpos_nodes + p + connection_dirs[dir];
1195 MapNode n2 = data->m_vmanip.getNodeNoEx(p2);
1196 if (nodedef->nodeboxConnects(n, n2, flag))
1197 neighbors_set |= flag;
1201 std::vector<aabb3f> boxes;
1202 n.getNodeBoxes(nodedef, &boxes, neighbors_set);
1203 for (std::vector<aabb3f>::iterator i = boxes.begin(); i != boxes.end(); ++i)
1204 drawAutoLightedCuboid(*i, NULL, tiles, 6);
1207 void MapblockMeshGenerator::drawMeshNode()
1211 bool private_mesh; // as a grab/drop pair is not thread-safe
1213 if (f->param_type_2 == CPT2_FACEDIR ||
1214 f->param_type_2 == CPT2_COLORED_FACEDIR) {
1215 facedir = n.getFaceDir(nodedef);
1216 } else if (f->param_type_2 == CPT2_WALLMOUNTED ||
1217 f->param_type_2 == CPT2_COLORED_WALLMOUNTED) {
1218 // Convert wallmounted to 6dfacedir.
1219 // When cache enabled, it is already converted.
1220 facedir = n.getWallMounted(nodedef);
1221 if (!enable_mesh_cache) {
1222 static const u8 wm_to_6d[6] = {20, 0, 16 + 1, 12 + 3, 8, 4 + 2};
1223 facedir = wm_to_6d[facedir];
1227 if (!data->m_smooth_lighting && f->mesh_ptr[facedir]) {
1228 // use cached meshes
1229 private_mesh = false;
1230 mesh = f->mesh_ptr[facedir];
1231 } else if (f->mesh_ptr[0]) {
1232 // no cache, clone and rotate mesh
1233 private_mesh = true;
1234 mesh = cloneMesh(f->mesh_ptr[0]);
1235 rotateMeshBy6dFacedir(mesh, facedir);
1236 recalculateBoundingBox(mesh);
1237 meshmanip->recalculateNormals(mesh, true, false);
1241 int mesh_buffer_count = mesh->getMeshBufferCount();
1242 for (int j = 0; j < mesh_buffer_count; j++) {
1244 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1245 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1246 int vertex_count = buf->getVertexCount();
1248 if (data->m_smooth_lighting) {
1249 // Mesh is always private here. So the lighting is applied to each
1250 // vertex right here.
1251 for (int k = 0; k < vertex_count; k++) {
1252 video::S3DVertex &vertex = vertices[k];
1253 vertex.Color = blendLight(vertex.Pos, vertex.Normal, tile.color);
1254 vertex.Pos += origin;
1256 collector->append(tile, vertices, vertex_count,
1257 buf->getIndices(), buf->getIndexCount());
1259 // Don't modify the mesh, it may not be private here.
1260 // Instead, let the collector process colors, etc.
1261 collector->append(tile, vertices, vertex_count,
1262 buf->getIndices(), buf->getIndexCount(), origin,
1263 color, f->light_source);
1270 // also called when the drawtype is known but should have been pre-converted
1271 void MapblockMeshGenerator::errorUnknownDrawtype()
1273 infostream << "Got drawtype " << f->drawtype << std::endl;
1274 FATAL_ERROR("Unknown drawtype");
1277 void MapblockMeshGenerator::drawNode()
1279 if (data->m_smooth_lighting)
1280 getSmoothLightFrame();
1282 light = getInteriorLight(n, 1, nodedef);
1283 switch (f->drawtype) {
1284 case NDT_LIQUID: drawLiquidNode(false); break;
1285 case NDT_FLOWINGLIQUID: drawLiquidNode(true); break;
1286 case NDT_GLASSLIKE: drawGlasslikeNode(); break;
1287 case NDT_GLASSLIKE_FRAMED: drawGlasslikeFramedNode(); break;
1288 case NDT_ALLFACES: drawAllfacesNode(); break;
1289 case NDT_TORCHLIKE: drawTorchlikeNode(); break;
1290 case NDT_SIGNLIKE: drawSignlikeNode(); break;
1291 case NDT_PLANTLIKE: drawPlantlikeNode(); break;
1292 case NDT_FIRELIKE: drawFirelikeNode(); break;
1293 case NDT_FENCELIKE: drawFencelikeNode(); break;
1294 case NDT_RAILLIKE: drawRaillikeNode(); break;
1295 case NDT_NODEBOX: drawNodeboxNode(); break;
1296 case NDT_MESH: drawMeshNode(); break;
1297 default: errorUnknownDrawtype(); break;
1302 TODO: Fix alpha blending for special nodes
1303 Currently only the last element rendered is blended correct
1305 void MapblockMeshGenerator::generate()
1307 for (p.Z = 0; p.Z < MAP_BLOCKSIZE; p.Z++)
1308 for (p.Y = 0; p.Y < MAP_BLOCKSIZE; p.Y++)
1309 for (p.X = 0; p.X < MAP_BLOCKSIZE; p.X++) {
1310 n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1311 f = &nodedef->get(n);
1312 // Solid nodes are drawn by MapBlockMesh
1313 if (f->solidness != 0)
1315 if (f->drawtype == NDT_AIRLIKE)
1317 origin = intToFloat(p, BS);