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, 0), v2f(1, 0), v2f(1, 1), v2f(0, 1)};
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())
636 v3f(-BS / 2, BS / 2, -BS / 2),
637 v3f( BS / 2, BS / 2, -BS / 2),
638 v3f( BS / 2, -BS / 2, -BS / 2),
639 v3f(-BS / 2, -BS / 2, -BS / 2),
641 for (int i = 0; i < 4; i++) {
643 case D6D_ZP: vertices[i].rotateXZBy(180); break;
644 case D6D_YP: vertices[i].rotateYZBy( 90); break;
645 case D6D_XP: vertices[i].rotateXZBy( 90); break;
646 case D6D_ZN: vertices[i].rotateXZBy( 0); break;
647 case D6D_YN: vertices[i].rotateYZBy(-90); break;
648 case D6D_XN: vertices[i].rotateXZBy(-90); break;
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 // Optionally render internal liquid level defined by param2
764 // Liquid is textured with 1 tile defined in nodedef 'special_tiles'
765 if (param2 > 0 && f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL &&
766 f->special_tiles[0].texture) {
767 // Internal liquid level has param2 range 0 .. 63,
768 // convert it to -0.5 .. 0.5
769 float vlev = (param2 / 63.0) * 2.0 - 1.0;
770 tile = getSpecialTile(*f, n, 0);
771 drawAutoLightedCuboid(aabb3f(-(nb[5] ? g : b),
775 (nb[1] ? g : b) * vlev,
780 void MapblockMeshGenerator::drawAllfacesNode()
782 static const aabb3f box(-BS / 2, -BS / 2, -BS / 2, BS / 2, BS / 2, BS / 2);
783 useDefaultTile(false);
784 drawAutoLightedCuboid(box);
787 void MapblockMeshGenerator::drawTorchlikeNode()
789 u8 wall = n.getWallMounted(nodedef);
792 case DWM_YP: tileindex = 1; break; // ceiling
793 case DWM_YN: tileindex = 0; break; // floor
794 default: tileindex = 2; // side (or invalid—should we care?)
796 useTile(tileindex, true);
798 float size = BS / 2 * f->visual_scale;
802 v3f( size, -size, 0),
803 v3f(-size, -size, 0),
805 for (int i = 0; i < 4; i++) {
807 case DWM_YP: vertices[i].rotateXZBy(-45); break;
808 case DWM_YN: vertices[i].rotateXZBy( 45); break;
809 case DWM_XP: vertices[i].rotateXZBy( 0); break;
810 case DWM_XN: vertices[i].rotateXZBy(180); break;
811 case DWM_ZP: vertices[i].rotateXZBy( 90); break;
812 case DWM_ZN: vertices[i].rotateXZBy(-90); break;
818 void MapblockMeshGenerator::drawSignlikeNode()
820 u8 wall = n.getWallMounted(nodedef);
822 static const float offset = BS / 16;
823 float size = BS / 2 * f->visual_scale;
824 // Wall at X+ of node
826 v3f(BS / 2 - offset, size, size),
827 v3f(BS / 2 - offset, size, -size),
828 v3f(BS / 2 - offset, -size, -size),
829 v3f(BS / 2 - offset, -size, size),
831 for (int i = 0; i < 4; i++) {
833 case DWM_YP: vertices[i].rotateXYBy( 90); break;
834 case DWM_YN: vertices[i].rotateXYBy(-90); break;
835 case DWM_XP: vertices[i].rotateXZBy( 0); break;
836 case DWM_XN: vertices[i].rotateXZBy(180); break;
837 case DWM_ZP: vertices[i].rotateXZBy( 90); break;
838 case DWM_ZN: vertices[i].rotateXZBy(-90); break;
844 void MapblockMeshGenerator::drawPlantlikeQuad(float rotation, float quad_offset,
845 bool offset_top_only)
848 v3f(-scale, -BS / 2 + scale * 2, 0),
849 v3f( scale, -BS / 2 + scale * 2, 0),
850 v3f( scale, -BS / 2, 0),
851 v3f(-scale, -BS / 2, 0),
853 if (random_offset_Y) {
854 PseudoRandom yrng(face_num++ | p.X << 16 | p.Z << 8 | p.Y << 24);
855 offset.Y = BS * ((yrng.next() % 16 / 16.0) * 0.125);
857 int offset_count = offset_top_only ? 2 : 4;
858 for (int i = 0; i < offset_count; i++)
859 vertices[i].Z += quad_offset;
860 for (int i = 0; i < 4; i++) {
861 vertices[i].rotateXZBy(rotation + rotate_degree);
862 vertices[i] += offset;
867 void MapblockMeshGenerator::drawPlantlikeNode()
870 draw_style = PLANT_STYLE_CROSS;
871 scale = BS / 2 * f->visual_scale;
872 offset = v3f(0, 0, 0);
874 random_offset_Y = false;
877 switch (f->param_type_2) {
878 case CPT2_MESHOPTIONS:
879 draw_style = PlantlikeStyle(n.param2 & MO_MASK_STYLE);
880 if (n.param2 & MO_BIT_SCALE_SQRT2)
882 if (n.param2 & MO_BIT_RANDOM_OFFSET) {
883 PseudoRandom rng(p.X << 8 | p.Z | p.Y << 16);
884 offset.X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
885 offset.Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
887 if (n.param2 & MO_BIT_RANDOM_OFFSET_Y)
888 random_offset_Y = true;
892 rotate_degree = n.param2 * 2;
899 switch (draw_style) {
900 case PLANT_STYLE_CROSS:
901 drawPlantlikeQuad(46);
902 drawPlantlikeQuad(-44);
905 case PLANT_STYLE_CROSS2:
906 drawPlantlikeQuad(91);
907 drawPlantlikeQuad(1);
910 case PLANT_STYLE_STAR:
911 drawPlantlikeQuad(121);
912 drawPlantlikeQuad(241);
913 drawPlantlikeQuad(1);
916 case PLANT_STYLE_HASH:
917 drawPlantlikeQuad( 1, BS / 4);
918 drawPlantlikeQuad( 91, BS / 4);
919 drawPlantlikeQuad(181, BS / 4);
920 drawPlantlikeQuad(271, BS / 4);
923 case PLANT_STYLE_HASH2:
924 drawPlantlikeQuad( 1, -BS / 2, true);
925 drawPlantlikeQuad( 91, -BS / 2, true);
926 drawPlantlikeQuad(181, -BS / 2, true);
927 drawPlantlikeQuad(271, -BS / 2, true);
932 void MapblockMeshGenerator::drawFirelikeQuad(float rotation, float opening_angle,
933 float offset_h, float offset_v)
936 v3f(-scale, -BS / 2 + scale * 2, 0),
937 v3f( scale, -BS / 2 + scale * 2, 0),
938 v3f( scale, -BS / 2, 0),
939 v3f(-scale, -BS / 2, 0),
941 for (int i = 0; i < 4; i++) {
942 vertices[i].rotateYZBy(opening_angle);
943 vertices[i].Z += offset_h;
944 vertices[i].rotateXZBy(rotation);
945 vertices[i].Y += offset_v;
950 void MapblockMeshGenerator::drawFirelikeNode()
953 scale = BS / 2 * f->visual_scale;
955 // Check for adjacent nodes
956 bool neighbors = false;
957 bool neighbor[6] = {0, 0, 0, 0, 0, 0};
958 content_t current = n.getContent();
959 for (int i = 0; i < 6; i++) {
960 v3s16 n2p = blockpos_nodes + p + g_6dirs[i];
961 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
962 content_t n2c = n2.getContent();
963 if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
968 bool drawBasicFire = neighbor[D6D_YN] || !neighbors;
969 bool drawBottomFire = neighbor[D6D_YP];
971 if (drawBasicFire || neighbor[D6D_ZP])
972 drawFirelikeQuad(0, -10, 0.4 * BS);
973 else if (drawBottomFire)
974 drawFirelikeQuad(0, 70, 0.47 * BS, 0.484 * BS);
976 if (drawBasicFire || neighbor[D6D_XN])
977 drawFirelikeQuad(90, -10, 0.4 * BS);
978 else if (drawBottomFire)
979 drawFirelikeQuad(90, 70, 0.47 * BS, 0.484 * BS);
981 if (drawBasicFire || neighbor[D6D_ZN])
982 drawFirelikeQuad(180, -10, 0.4 * BS);
983 else if (drawBottomFire)
984 drawFirelikeQuad(180, 70, 0.47 * BS, 0.484 * BS);
986 if (drawBasicFire || neighbor[D6D_XP])
987 drawFirelikeQuad(270, -10, 0.4 * BS);
988 else if (drawBottomFire)
989 drawFirelikeQuad(270, 70, 0.47 * BS, 0.484 * BS);
992 drawFirelikeQuad(45, 0, 0.0);
993 drawFirelikeQuad(-45, 0, 0.0);
997 void MapblockMeshGenerator::drawFencelikeNode()
999 useDefaultTile(false);
1000 TileSpec tile_nocrack = tile;
1001 tile_nocrack.material_flags &= ~MATERIAL_FLAG_CRACK;
1003 // Put wood the right way around in the posts
1004 TileSpec tile_rot = tile;
1005 tile_rot.rotation = 1;
1007 static const f32 post_rad = BS / 8;
1008 static const f32 bar_rad = BS / 16;
1009 static const f32 bar_len = BS / 2 - post_rad;
1011 // The post - always present
1012 static const aabb3f post(-post_rad, -BS / 2, -post_rad,
1013 post_rad, BS / 2, post_rad);
1014 static const f32 postuv[24] = {
1015 0.375, 0.375, 0.625, 0.625,
1016 0.375, 0.375, 0.625, 0.625,
1017 0.000, 0.000, 0.250, 1.000,
1018 0.250, 0.000, 0.500, 1.000,
1019 0.500, 0.000, 0.750, 1.000,
1020 0.750, 0.000, 1.000, 1.000,
1023 drawAutoLightedCuboid(post, postuv);
1025 tile = tile_nocrack;
1027 // Now a section of fence, +X, if there's a post there
1030 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1031 const ContentFeatures *f2 = &nodedef->get(n2);
1032 if (f2->drawtype == NDT_FENCELIKE) {
1033 static const aabb3f bar_x1(BS / 2 - bar_len, BS / 4 - bar_rad, -bar_rad,
1034 BS / 2 + bar_len, BS / 4 + bar_rad, bar_rad);
1035 static const aabb3f bar_x2(BS / 2 - bar_len, -BS / 4 - bar_rad, -bar_rad,
1036 BS / 2 + bar_len, -BS / 4 + bar_rad, bar_rad);
1037 static const f32 xrailuv[24] = {
1038 0.000, 0.125, 1.000, 0.250,
1039 0.000, 0.250, 1.000, 0.375,
1040 0.375, 0.375, 0.500, 0.500,
1041 0.625, 0.625, 0.750, 0.750,
1042 0.000, 0.500, 1.000, 0.625,
1043 0.000, 0.875, 1.000, 1.000,
1045 drawAutoLightedCuboid(bar_x1, xrailuv);
1046 drawAutoLightedCuboid(bar_x2, xrailuv);
1049 // Now a section of fence, +Z, if there's a post there
1052 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1053 f2 = &nodedef->get(n2);
1054 if (f2->drawtype == NDT_FENCELIKE) {
1055 static const aabb3f bar_z1(-bar_rad, BS / 4 - bar_rad, BS / 2 - bar_len,
1056 bar_rad, BS / 4 + bar_rad, BS / 2 + bar_len);
1057 static const aabb3f bar_z2(-bar_rad, -BS / 4 - bar_rad, BS / 2 - bar_len,
1058 bar_rad, -BS / 4 + bar_rad, BS / 2 + bar_len);
1059 static const f32 zrailuv[24] = {
1060 0.1875, 0.0625, 0.3125, 0.3125, // cannot rotate; stretch
1061 0.2500, 0.0625, 0.3750, 0.3125, // for wood texture instead
1062 0.0000, 0.5625, 1.0000, 0.6875,
1063 0.0000, 0.3750, 1.0000, 0.5000,
1064 0.3750, 0.3750, 0.5000, 0.5000,
1065 0.6250, 0.6250, 0.7500, 0.7500,
1067 drawAutoLightedCuboid(bar_z1, zrailuv);
1068 drawAutoLightedCuboid(bar_z2, zrailuv);
1072 bool MapblockMeshGenerator::isSameRail(v3s16 dir)
1074 MapNode node2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
1075 if (node2.getContent() == n.getContent())
1077 const ContentFeatures &def2 = nodedef->get(node2);
1078 return ((def2.drawtype == NDT_RAILLIKE) &&
1079 (def2.getGroup(raillike_groupname) == raillike_group));
1082 void MapblockMeshGenerator::drawRaillikeNode()
1084 static const v3s16 direction[4] = {
1090 static const int slope_angle[4] = {0, 180, 90, -90};
1102 static const RailDesc rail_kinds[16] = {
1105 {straight, 0}, // . . . .
1106 {straight, 0}, // . . . +Z
1107 {straight, 0}, // . . -Z .
1108 {straight, 0}, // . . -Z +Z
1109 {straight, 90}, // . -X . .
1110 { curved, 180}, // . -X . +Z
1111 { curved, 270}, // . -X -Z .
1112 {junction, 180}, // . -X -Z +Z
1113 {straight, 90}, // +X . . .
1114 { curved, 90}, // +X . . +Z
1115 { curved, 0}, // +X . -Z .
1116 {junction, 0}, // +X . -Z +Z
1117 {straight, 90}, // +X -X . .
1118 {junction, 90}, // +X -X . +Z
1119 {junction, 270}, // +X -X -Z .
1120 { cross, 0}, // +X -X -Z +Z
1123 raillike_group = nodedef->get(n).getGroup(raillike_groupname);
1128 bool sloped = false;
1129 for (int dir = 0; dir < 4; dir++) {
1130 bool rail_above = isSameRail(direction[dir] + v3s16(0, 1, 0));
1133 angle = slope_angle[dir];
1136 isSameRail(direction[dir]) ||
1137 isSameRail(direction[dir] + v3s16(0, -1, 0)))
1142 tile_index = straight;
1144 tile_index = rail_kinds[code].tile_index;
1145 angle = rail_kinds[code].angle;
1148 useTile(tile_index, true);
1150 static const float offset = BS / 64;
1151 static const float size = BS / 2;
1152 float y2 = sloped ? size : -size;
1154 v3f(-size, y2 + offset, size),
1155 v3f( size, y2 + offset, size),
1156 v3f( size, -size + offset, -size),
1157 v3f(-size, -size + offset, -size),
1160 for (int i = 0; i < 4; i++)
1161 vertices[i].rotateXZBy(angle);
1165 void MapblockMeshGenerator::drawNodeboxNode()
1167 static const v3s16 tile_dirs[6] = {
1176 // we have this order for some reason...
1177 static const v3s16 connection_dirs[6] = {
1178 v3s16( 0, 1, 0), // top
1179 v3s16( 0, -1, 0), // bottom
1180 v3s16( 0, 0, -1), // front
1181 v3s16(-1, 0, 0), // left
1182 v3s16( 0, 0, 1), // back
1183 v3s16( 1, 0, 0), // right
1187 for (int face = 0; face < 6; face++) {
1188 // Handles facedir rotation for textures
1189 tiles[face] = getTile(tile_dirs[face]);
1192 // locate possible neighboring nodes to connect to
1193 int neighbors_set = 0;
1194 if (f->node_box.type == NODEBOX_CONNECTED) {
1195 for (int dir = 0; dir != 6; dir++) {
1196 int flag = 1 << dir;
1197 v3s16 p2 = blockpos_nodes + p + connection_dirs[dir];
1198 MapNode n2 = data->m_vmanip.getNodeNoEx(p2);
1199 if (nodedef->nodeboxConnects(n, n2, flag))
1200 neighbors_set |= flag;
1204 std::vector<aabb3f> boxes;
1205 n.getNodeBoxes(nodedef, &boxes, neighbors_set);
1206 for (std::vector<aabb3f>::iterator i = boxes.begin(); i != boxes.end(); ++i)
1207 drawAutoLightedCuboid(*i, NULL, tiles, 6);
1210 void MapblockMeshGenerator::drawMeshNode()
1214 bool private_mesh; // as a grab/drop pair is not thread-safe
1216 if (f->param_type_2 == CPT2_FACEDIR ||
1217 f->param_type_2 == CPT2_COLORED_FACEDIR) {
1218 facedir = n.getFaceDir(nodedef);
1219 } else if (f->param_type_2 == CPT2_WALLMOUNTED ||
1220 f->param_type_2 == CPT2_COLORED_WALLMOUNTED) {
1221 // Convert wallmounted to 6dfacedir.
1222 // When cache enabled, it is already converted.
1223 facedir = n.getWallMounted(nodedef);
1224 if (!enable_mesh_cache) {
1225 static const u8 wm_to_6d[6] = {20, 0, 16 + 1, 12 + 3, 8, 4 + 2};
1226 facedir = wm_to_6d[facedir];
1230 if (!data->m_smooth_lighting && f->mesh_ptr[facedir]) {
1231 // use cached meshes
1232 private_mesh = false;
1233 mesh = f->mesh_ptr[facedir];
1234 } else if (f->mesh_ptr[0]) {
1235 // no cache, clone and rotate mesh
1236 private_mesh = true;
1237 mesh = cloneMesh(f->mesh_ptr[0]);
1238 rotateMeshBy6dFacedir(mesh, facedir);
1239 recalculateBoundingBox(mesh);
1240 meshmanip->recalculateNormals(mesh, true, false);
1244 int mesh_buffer_count = mesh->getMeshBufferCount();
1245 for (int j = 0; j < mesh_buffer_count; j++) {
1247 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1248 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1249 int vertex_count = buf->getVertexCount();
1251 if (data->m_smooth_lighting) {
1252 // Mesh is always private here. So the lighting is applied to each
1253 // vertex right here.
1254 for (int k = 0; k < vertex_count; k++) {
1255 video::S3DVertex &vertex = vertices[k];
1256 vertex.Color = blendLight(vertex.Pos, vertex.Normal, tile.color);
1257 vertex.Pos += origin;
1259 collector->append(tile, vertices, vertex_count,
1260 buf->getIndices(), buf->getIndexCount());
1262 // Don't modify the mesh, it may not be private here.
1263 // Instead, let the collector process colors, etc.
1264 collector->append(tile, vertices, vertex_count,
1265 buf->getIndices(), buf->getIndexCount(), origin,
1266 color, f->light_source);
1273 // also called when the drawtype is known but should have been pre-converted
1274 void MapblockMeshGenerator::errorUnknownDrawtype()
1276 infostream << "Got drawtype " << f->drawtype << std::endl;
1277 FATAL_ERROR("Unknown drawtype");
1280 void MapblockMeshGenerator::drawNode()
1282 if (data->m_smooth_lighting)
1283 getSmoothLightFrame();
1285 light = getInteriorLight(n, 1, nodedef);
1286 switch (f->drawtype) {
1287 case NDT_LIQUID: drawLiquidNode(false); break;
1288 case NDT_FLOWINGLIQUID: drawLiquidNode(true); break;
1289 case NDT_GLASSLIKE: drawGlasslikeNode(); break;
1290 case NDT_GLASSLIKE_FRAMED: drawGlasslikeFramedNode(); break;
1291 case NDT_ALLFACES: drawAllfacesNode(); break;
1292 case NDT_TORCHLIKE: drawTorchlikeNode(); break;
1293 case NDT_SIGNLIKE: drawSignlikeNode(); break;
1294 case NDT_PLANTLIKE: drawPlantlikeNode(); break;
1295 case NDT_FIRELIKE: drawFirelikeNode(); break;
1296 case NDT_FENCELIKE: drawFencelikeNode(); break;
1297 case NDT_RAILLIKE: drawRaillikeNode(); break;
1298 case NDT_NODEBOX: drawNodeboxNode(); break;
1299 case NDT_MESH: drawMeshNode(); break;
1300 default: errorUnknownDrawtype(); break;
1305 TODO: Fix alpha blending for special nodes
1306 Currently only the last element rendered is blended correct
1308 void MapblockMeshGenerator::generate()
1310 for (p.Z = 0; p.Z < MAP_BLOCKSIZE; p.Z++)
1311 for (p.Y = 0; p.Y < MAP_BLOCKSIZE; p.Y++)
1312 for (p.X = 0; p.X < MAP_BLOCKSIZE; p.X++) {
1313 n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1314 f = &nodedef->get(n);
1315 // Solid nodes are drawn by MapBlockMesh
1316 if (f->solidness != 0)
1318 if (f->drawtype == NDT_AIRLIKE)
1320 origin = intToFloat(p, BS);