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()
381 tile_liquid_top = getSpecialTile(*f, n, 0);
382 tile_liquid = getSpecialTile(*f, n, 1);
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()
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 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::calculateCornerLevels()
448 for (int k = 0; k < 2; k++)
449 for (int i = 0; i < 2; i++)
450 corner_levels[k][i] = getCornerLevel(i, k);
453 f32 MapblockMeshGenerator::getCornerLevel(int i, int k)
458 for (int dk = 0; dk < 2; dk++)
459 for (int di = 0; di < 2; di++) {
460 NeighborData &neighbor_data = liquid_neighbors[k + dk][i + di];
461 content_t content = neighbor_data.content;
463 // If top is liquid, draw starting from top of node
464 if (neighbor_data.top_is_same_liquid)
467 // Source always has the full height
468 if (content == c_source)
471 // Flowing liquid has level information
472 if (content == c_flowing) {
473 sum += neighbor_data.level;
475 } else if (content == CONTENT_AIR) {
478 return -0.5 * BS + 0.2;
486 void MapblockMeshGenerator::drawLiquidSides()
488 struct LiquidFaceDesc {
490 v3s16 p[2]; // XZ only; 1 means +, 0 means -
495 static const LiquidFaceDesc base_faces[4] = {
496 {v3s16( 1, 0, 0), {v3s16(1, 0, 1), v3s16(1, 0, 0)}},
497 {v3s16(-1, 0, 0), {v3s16(0, 0, 0), v3s16(0, 0, 1)}},
498 {v3s16( 0, 0, 1), {v3s16(0, 0, 1), v3s16(1, 0, 1)}},
499 {v3s16( 0, 0, -1), {v3s16(1, 0, 0), v3s16(0, 0, 0)}},
501 static const UV base_vertices[4] = {
507 for (int i = 0; i < 4; i++) {
508 const LiquidFaceDesc &face = base_faces[i];
509 const NeighborData &neighbor = liquid_neighbors[face.dir.Z + 1][face.dir.X + 1];
511 // No face between nodes of the same liquid, unless there is node
512 // at the top to which it should be connected. Again, unless the face
513 // there would be inside the liquid
514 if (neighbor.is_same_liquid) {
515 if (!top_is_same_liquid)
517 if (neighbor.top_is_same_liquid)
521 const ContentFeatures &neighbor_features = nodedef->get(neighbor.content);
522 // Don't draw face if neighbor is blocking the view
523 if (neighbor_features.solidness == 2)
526 video::S3DVertex vertices[4];
527 for (int j = 0; j < 4; j++) {
528 const UV &vertex = base_vertices[j];
529 const v3s16 &base = face.p[vertex.u];
531 pos.X = (base.X - 0.5) * BS;
532 pos.Z = (base.Z - 0.5) * BS;
534 pos.Y = neighbor.is_same_liquid ? corner_levels[base.Z][base.X] : -0.5 * BS;
536 pos.Y = !top_is_same_liquid ? corner_levels[base.Z][base.X] : 0.5 * BS;
537 if (data->m_smooth_lighting)
538 color = blendLightColor(pos);
540 vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, color, vertex.u, vertex.v);
542 collector->append(tile_liquid, vertices, 4, quad_indices, 6);
546 void MapblockMeshGenerator::drawLiquidTop()
548 // To get backface culling right, the vertices need to go
549 // clockwise around the front of the face. And we happened to
550 // calculate corner levels in exact reverse order.
551 static const int corner_resolve[4][2] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
553 video::S3DVertex vertices[4] = {
554 video::S3DVertex(-BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 0, 1),
555 video::S3DVertex( BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 1, 1),
556 video::S3DVertex( BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 1, 0),
557 video::S3DVertex(-BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 0, 0),
560 for (int i = 0; i < 4; i++) {
561 int u = corner_resolve[i][0];
562 int w = corner_resolve[i][1];
563 vertices[i].Pos.Y += corner_levels[w][u];
564 if (data->m_smooth_lighting)
565 vertices[i].Color = blendLightColor(vertices[i].Pos);
566 vertices[i].Pos += origin;
569 // Default downwards-flowing texture animation goes from
570 // -Z towards +Z, thus the direction is +Z.
571 // Rotate texture to make animation go in flow direction
572 // Positive if liquid moves towards +Z
573 f32 dz = (corner_levels[0][0] + corner_levels[0][1]) -
574 (corner_levels[1][0] + corner_levels[1][1]);
575 // Positive if liquid moves towards +X
576 f32 dx = (corner_levels[0][0] + corner_levels[1][0]) -
577 (corner_levels[0][1] + corner_levels[1][1]);
578 f32 tcoord_angle = atan2(dz, dx) * core::RADTODEG;
579 v2f tcoord_center(0.5, 0.5);
580 v2f tcoord_translate(blockpos_nodes.Z + p.Z, blockpos_nodes.X + p.X);
581 tcoord_translate.rotateBy(tcoord_angle);
582 tcoord_translate.X -= floor(tcoord_translate.X);
583 tcoord_translate.Y -= floor(tcoord_translate.Y);
585 for (int i = 0; i < 4; i++) {
586 vertices[i].TCoords.rotateBy(tcoord_angle, tcoord_center);
587 vertices[i].TCoords += tcoord_translate;
590 std::swap(vertices[0].TCoords, vertices[2].TCoords);
592 collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
595 void MapblockMeshGenerator::drawLiquidNode()
597 prepareLiquidNodeDrawing();
598 getLiquidNeighborhood();
599 calculateCornerLevels();
601 if (!top_is_same_liquid)
605 void MapblockMeshGenerator::drawGlasslikeNode()
609 for (int face = 0; face < 6; face++) {
610 // Check this neighbor
611 v3s16 dir = g_6dirs[face];
612 v3s16 neighbor_pos = blockpos_nodes + p + dir;
613 MapNode neighbor = data->m_vmanip.getNodeNoExNoEmerge(neighbor_pos);
614 // Don't make face if neighbor is of same type
615 if (neighbor.getContent() == n.getContent())
619 v3f(-BS / 2, BS / 2, -BS / 2),
620 v3f( BS / 2, BS / 2, -BS / 2),
621 v3f( BS / 2, -BS / 2, -BS / 2),
622 v3f(-BS / 2, -BS / 2, -BS / 2),
624 for (int i = 0; i < 4; i++) {
626 case D6D_ZP: vertices[i].rotateXZBy(180); break;
627 case D6D_YP: vertices[i].rotateYZBy( 90); break;
628 case D6D_XP: vertices[i].rotateXZBy( 90); break;
629 case D6D_ZN: vertices[i].rotateXZBy( 0); break;
630 case D6D_YN: vertices[i].rotateYZBy(-90); break;
631 case D6D_XN: vertices[i].rotateXZBy(-90); break;
634 drawQuad(vertices, dir);
638 void MapblockMeshGenerator::drawGlasslikeFramedNode()
641 for (int face = 0; face < 6; face++)
642 getTile(g_6dirs[face], tiles[face]);
644 TileSpec glass_tiles[6];
645 if (tiles[1].layers[0].texture &&
646 tiles[2].layers[0].texture &&
647 tiles[3].layers[0].texture) {
648 glass_tiles[0] = tiles[4];
649 glass_tiles[1] = tiles[0];
650 glass_tiles[2] = tiles[4];
651 glass_tiles[3] = tiles[4];
652 glass_tiles[4] = tiles[3];
653 glass_tiles[5] = tiles[4];
655 for (int face = 0; face < 6; face++)
656 glass_tiles[face] = tiles[4];
659 u8 param2 = n.getParam2();
660 bool H_merge = !(param2 & 128);
661 bool V_merge = !(param2 & 64);
664 static const float a = BS / 2;
665 static const float g = a - 0.003;
666 static const float b = .876 * ( BS / 2 );
668 static const aabb3f frame_edges[FRAMED_EDGE_COUNT] = {
669 aabb3f( b, b, -a, a, a, a), // y+
670 aabb3f(-a, b, -a, -b, a, a), // y+
671 aabb3f( b, -a, -a, a, -b, a), // y-
672 aabb3f(-a, -a, -a, -b, -b, a), // y-
673 aabb3f( b, -a, b, a, a, a), // x+
674 aabb3f( b, -a, -a, a, a, -b), // x+
675 aabb3f(-a, -a, b, -b, a, a), // x-
676 aabb3f(-a, -a, -a, -b, a, -b), // x-
677 aabb3f(-a, b, b, a, a, a), // z+
678 aabb3f(-a, -a, b, a, -b, a), // z+
679 aabb3f(-a, -a, -a, a, -b, -b), // z-
680 aabb3f(-a, b, -a, a, a, -b), // z-
682 static const aabb3f glass_faces[6] = {
683 aabb3f(-g, -g, g, g, g, g), // z+
684 aabb3f(-g, g, -g, g, g, g), // y+
685 aabb3f( g, -g, -g, g, g, g), // x+
686 aabb3f(-g, -g, -g, g, g, -g), // z-
687 aabb3f(-g, -g, -g, g, -g, g), // y-
688 aabb3f(-g, -g, -g, -g, g, g), // x-
691 // tables of neighbour (connect if same type and merge allowed),
692 // checked with g_26dirs
694 // 1 = connect, 0 = face visible
695 bool nb[FRAMED_NEIGHBOR_COUNT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
698 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};
699 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};
700 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};
701 const bool *check_nb = check_nb_all;
703 // neighbours checks for frames visibility
704 if (H_merge || V_merge) {
706 check_nb = check_nb_vertical; // vertical-only merge
708 check_nb = check_nb_horizontal; // horizontal-only merge
709 content_t current = n.getContent();
710 for (int i = 0; i < FRAMED_NEIGHBOR_COUNT; i++) {
713 v3s16 n2p = blockpos_nodes + p + g_26dirs[i];
714 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
715 content_t n2c = n2.getContent();
716 if (n2c == current || n2c == CONTENT_IGNORE)
723 static const u8 nb_triplet[FRAMED_EDGE_COUNT][3] = {
724 {1, 2, 7}, {1, 5, 6}, {4, 2, 15}, {4, 5, 14},
725 {2, 0, 11}, {2, 3, 13}, {5, 0, 10}, {5, 3, 12},
726 {0, 1, 8}, {0, 4, 16}, {3, 4, 17}, {3, 1, 9},
730 for (int edge = 0; edge < FRAMED_EDGE_COUNT; edge++) {
732 if (nb[nb_triplet[edge][2]])
733 edge_invisible = nb[nb_triplet[edge][0]] & nb[nb_triplet[edge][1]];
735 edge_invisible = nb[nb_triplet[edge][0]] ^ nb[nb_triplet[edge][1]];
738 drawAutoLightedCuboid(frame_edges[edge]);
741 for (int face = 0; face < 6; face++) {
744 tile = glass_tiles[face];
745 drawAutoLightedCuboid(glass_faces[face]);
748 // Optionally render internal liquid level defined by param2
749 // Liquid is textured with 1 tile defined in nodedef 'special_tiles'
750 if (param2 > 0 && f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL &&
751 f->special_tiles[0].layers[0].texture) {
752 // Internal liquid level has param2 range 0 .. 63,
753 // convert it to -0.5 .. 0.5
754 float vlev = (param2 / 63.0) * 2.0 - 1.0;
755 tile = getSpecialTile(*f, n, 0);
756 drawAutoLightedCuboid(aabb3f(-(nb[5] ? g : b),
760 (nb[1] ? g : b) * vlev,
765 void MapblockMeshGenerator::drawAllfacesNode()
767 static const aabb3f box(-BS / 2, -BS / 2, -BS / 2, BS / 2, BS / 2, BS / 2);
768 useDefaultTile(false);
769 drawAutoLightedCuboid(box);
772 void MapblockMeshGenerator::drawTorchlikeNode()
774 u8 wall = n.getWallMounted(nodedef);
777 case DWM_YP: tileindex = 1; break; // ceiling
778 case DWM_YN: tileindex = 0; break; // floor
779 default: tileindex = 2; // side (or invalid—should we care?)
781 useTile(tileindex, true);
783 float size = BS / 2 * f->visual_scale;
787 v3f( size, -size, 0),
788 v3f(-size, -size, 0),
790 for (int i = 0; i < 4; i++) {
792 case DWM_YP: vertices[i].rotateXZBy(-45); break;
793 case DWM_YN: vertices[i].rotateXZBy( 45); break;
794 case DWM_XP: vertices[i].rotateXZBy( 0); break;
795 case DWM_XN: vertices[i].rotateXZBy(180); break;
796 case DWM_ZP: vertices[i].rotateXZBy( 90); break;
797 case DWM_ZN: vertices[i].rotateXZBy(-90); break;
803 void MapblockMeshGenerator::drawSignlikeNode()
805 u8 wall = n.getWallMounted(nodedef);
807 static const float offset = BS / 16;
808 float size = BS / 2 * f->visual_scale;
809 // Wall at X+ of node
811 v3f(BS / 2 - offset, size, size),
812 v3f(BS / 2 - offset, size, -size),
813 v3f(BS / 2 - offset, -size, -size),
814 v3f(BS / 2 - offset, -size, size),
816 for (int i = 0; i < 4; i++) {
818 case DWM_YP: vertices[i].rotateXYBy( 90); break;
819 case DWM_YN: vertices[i].rotateXYBy(-90); break;
820 case DWM_XP: vertices[i].rotateXZBy( 0); break;
821 case DWM_XN: vertices[i].rotateXZBy(180); break;
822 case DWM_ZP: vertices[i].rotateXZBy( 90); break;
823 case DWM_ZN: vertices[i].rotateXZBy(-90); break;
829 void MapblockMeshGenerator::drawPlantlikeQuad(float rotation, float quad_offset,
830 bool offset_top_only)
833 v3f(-scale, -BS / 2 + scale * 2, 0),
834 v3f( scale, -BS / 2 + scale * 2, 0),
835 v3f( scale, -BS / 2, 0),
836 v3f(-scale, -BS / 2, 0),
838 if (random_offset_Y) {
839 PseudoRandom yrng(face_num++ | p.X << 16 | p.Z << 8 | p.Y << 24);
840 offset.Y = BS * ((yrng.next() % 16 / 16.0) * 0.125);
842 int offset_count = offset_top_only ? 2 : 4;
843 for (int i = 0; i < offset_count; i++)
844 vertices[i].Z += quad_offset;
845 for (int i = 0; i < 4; i++) {
846 vertices[i].rotateXZBy(rotation + rotate_degree);
847 vertices[i] += offset;
852 void MapblockMeshGenerator::drawPlantlikeNode()
855 draw_style = PLANT_STYLE_CROSS;
856 scale = BS / 2 * f->visual_scale;
857 offset = v3f(0, 0, 0);
859 random_offset_Y = false;
862 switch (f->param_type_2) {
863 case CPT2_MESHOPTIONS:
864 draw_style = PlantlikeStyle(n.param2 & MO_MASK_STYLE);
865 if (n.param2 & MO_BIT_SCALE_SQRT2)
867 if (n.param2 & MO_BIT_RANDOM_OFFSET) {
868 PseudoRandom rng(p.X << 8 | p.Z | p.Y << 16);
869 offset.X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
870 offset.Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
872 if (n.param2 & MO_BIT_RANDOM_OFFSET_Y)
873 random_offset_Y = true;
877 rotate_degree = n.param2 * 2;
884 switch (draw_style) {
885 case PLANT_STYLE_CROSS:
886 drawPlantlikeQuad(46);
887 drawPlantlikeQuad(-44);
890 case PLANT_STYLE_CROSS2:
891 drawPlantlikeQuad(91);
892 drawPlantlikeQuad(1);
895 case PLANT_STYLE_STAR:
896 drawPlantlikeQuad(121);
897 drawPlantlikeQuad(241);
898 drawPlantlikeQuad(1);
901 case PLANT_STYLE_HASH:
902 drawPlantlikeQuad( 1, BS / 4);
903 drawPlantlikeQuad( 91, BS / 4);
904 drawPlantlikeQuad(181, BS / 4);
905 drawPlantlikeQuad(271, BS / 4);
908 case PLANT_STYLE_HASH2:
909 drawPlantlikeQuad( 1, -BS / 2, true);
910 drawPlantlikeQuad( 91, -BS / 2, true);
911 drawPlantlikeQuad(181, -BS / 2, true);
912 drawPlantlikeQuad(271, -BS / 2, true);
917 void MapblockMeshGenerator::drawFirelikeQuad(float rotation, float opening_angle,
918 float offset_h, float offset_v)
921 v3f(-scale, -BS / 2 + scale * 2, 0),
922 v3f( scale, -BS / 2 + scale * 2, 0),
923 v3f( scale, -BS / 2, 0),
924 v3f(-scale, -BS / 2, 0),
926 for (int i = 0; i < 4; i++) {
927 vertices[i].rotateYZBy(opening_angle);
928 vertices[i].Z += offset_h;
929 vertices[i].rotateXZBy(rotation);
930 vertices[i].Y += offset_v;
935 void MapblockMeshGenerator::drawFirelikeNode()
938 scale = BS / 2 * f->visual_scale;
940 // Check for adjacent nodes
941 bool neighbors = false;
942 bool neighbor[6] = {0, 0, 0, 0, 0, 0};
943 content_t current = n.getContent();
944 for (int i = 0; i < 6; i++) {
945 v3s16 n2p = blockpos_nodes + p + g_6dirs[i];
946 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
947 content_t n2c = n2.getContent();
948 if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
953 bool drawBasicFire = neighbor[D6D_YN] || !neighbors;
954 bool drawBottomFire = neighbor[D6D_YP];
956 if (drawBasicFire || neighbor[D6D_ZP])
957 drawFirelikeQuad(0, -10, 0.4 * BS);
958 else if (drawBottomFire)
959 drawFirelikeQuad(0, 70, 0.47 * BS, 0.484 * BS);
961 if (drawBasicFire || neighbor[D6D_XN])
962 drawFirelikeQuad(90, -10, 0.4 * BS);
963 else if (drawBottomFire)
964 drawFirelikeQuad(90, 70, 0.47 * BS, 0.484 * BS);
966 if (drawBasicFire || neighbor[D6D_ZN])
967 drawFirelikeQuad(180, -10, 0.4 * BS);
968 else if (drawBottomFire)
969 drawFirelikeQuad(180, 70, 0.47 * BS, 0.484 * BS);
971 if (drawBasicFire || neighbor[D6D_XP])
972 drawFirelikeQuad(270, -10, 0.4 * BS);
973 else if (drawBottomFire)
974 drawFirelikeQuad(270, 70, 0.47 * BS, 0.484 * BS);
977 drawFirelikeQuad(45, 0, 0.0);
978 drawFirelikeQuad(-45, 0, 0.0);
982 void MapblockMeshGenerator::drawFencelikeNode()
984 useDefaultTile(false);
985 TileSpec tile_nocrack = tile;
986 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++)
987 tile_nocrack.layers[layer].material_flags &= ~MATERIAL_FLAG_CRACK;
989 // Put wood the right way around in the posts
990 TileSpec tile_rot = tile;
991 tile_rot.rotation = 1;
993 static const f32 post_rad = BS / 8;
994 static const f32 bar_rad = BS / 16;
995 static const f32 bar_len = BS / 2 - post_rad;
997 // The post - always present
998 static const aabb3f post(-post_rad, -BS / 2, -post_rad,
999 post_rad, BS / 2, post_rad);
1000 static const f32 postuv[24] = {
1001 0.375, 0.375, 0.625, 0.625,
1002 0.375, 0.375, 0.625, 0.625,
1003 0.000, 0.000, 0.250, 1.000,
1004 0.250, 0.000, 0.500, 1.000,
1005 0.500, 0.000, 0.750, 1.000,
1006 0.750, 0.000, 1.000, 1.000,
1009 drawAutoLightedCuboid(post, postuv);
1011 tile = tile_nocrack;
1013 // Now a section of fence, +X, if there's a post there
1016 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1017 const ContentFeatures *f2 = &nodedef->get(n2);
1018 if (f2->drawtype == NDT_FENCELIKE) {
1019 static const aabb3f bar_x1(BS / 2 - bar_len, BS / 4 - bar_rad, -bar_rad,
1020 BS / 2 + bar_len, BS / 4 + bar_rad, bar_rad);
1021 static const aabb3f bar_x2(BS / 2 - bar_len, -BS / 4 - bar_rad, -bar_rad,
1022 BS / 2 + bar_len, -BS / 4 + bar_rad, bar_rad);
1023 static const f32 xrailuv[24] = {
1024 0.000, 0.125, 1.000, 0.250,
1025 0.000, 0.250, 1.000, 0.375,
1026 0.375, 0.375, 0.500, 0.500,
1027 0.625, 0.625, 0.750, 0.750,
1028 0.000, 0.500, 1.000, 0.625,
1029 0.000, 0.875, 1.000, 1.000,
1031 drawAutoLightedCuboid(bar_x1, xrailuv);
1032 drawAutoLightedCuboid(bar_x2, xrailuv);
1035 // Now a section of fence, +Z, if there's a post there
1038 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1039 f2 = &nodedef->get(n2);
1040 if (f2->drawtype == NDT_FENCELIKE) {
1041 static const aabb3f bar_z1(-bar_rad, BS / 4 - bar_rad, BS / 2 - bar_len,
1042 bar_rad, BS / 4 + bar_rad, BS / 2 + bar_len);
1043 static const aabb3f bar_z2(-bar_rad, -BS / 4 - bar_rad, BS / 2 - bar_len,
1044 bar_rad, -BS / 4 + bar_rad, BS / 2 + bar_len);
1045 static const f32 zrailuv[24] = {
1046 0.1875, 0.0625, 0.3125, 0.3125, // cannot rotate; stretch
1047 0.2500, 0.0625, 0.3750, 0.3125, // for wood texture instead
1048 0.0000, 0.5625, 1.0000, 0.6875,
1049 0.0000, 0.3750, 1.0000, 0.5000,
1050 0.3750, 0.3750, 0.5000, 0.5000,
1051 0.6250, 0.6250, 0.7500, 0.7500,
1053 drawAutoLightedCuboid(bar_z1, zrailuv);
1054 drawAutoLightedCuboid(bar_z2, zrailuv);
1058 bool MapblockMeshGenerator::isSameRail(v3s16 dir)
1060 MapNode node2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
1061 if (node2.getContent() == n.getContent())
1063 const ContentFeatures &def2 = nodedef->get(node2);
1064 return ((def2.drawtype == NDT_RAILLIKE) &&
1065 (def2.getGroup(raillike_groupname) == raillike_group));
1068 void MapblockMeshGenerator::drawRaillikeNode()
1070 static const v3s16 direction[4] = {
1076 static const int slope_angle[4] = {0, 180, 90, -90};
1088 static const RailDesc rail_kinds[16] = {
1091 {straight, 0}, // . . . .
1092 {straight, 0}, // . . . +Z
1093 {straight, 0}, // . . -Z .
1094 {straight, 0}, // . . -Z +Z
1095 {straight, 90}, // . -X . .
1096 { curved, 180}, // . -X . +Z
1097 { curved, 270}, // . -X -Z .
1098 {junction, 180}, // . -X -Z +Z
1099 {straight, 90}, // +X . . .
1100 { curved, 90}, // +X . . +Z
1101 { curved, 0}, // +X . -Z .
1102 {junction, 0}, // +X . -Z +Z
1103 {straight, 90}, // +X -X . .
1104 {junction, 90}, // +X -X . +Z
1105 {junction, 270}, // +X -X -Z .
1106 { cross, 0}, // +X -X -Z +Z
1109 raillike_group = nodedef->get(n).getGroup(raillike_groupname);
1114 bool sloped = false;
1115 for (int dir = 0; dir < 4; dir++) {
1116 bool rail_above = isSameRail(direction[dir] + v3s16(0, 1, 0));
1119 angle = slope_angle[dir];
1122 isSameRail(direction[dir]) ||
1123 isSameRail(direction[dir] + v3s16(0, -1, 0)))
1128 tile_index = straight;
1130 tile_index = rail_kinds[code].tile_index;
1131 angle = rail_kinds[code].angle;
1134 useTile(tile_index, true);
1136 static const float offset = BS / 64;
1137 static const float size = BS / 2;
1138 float y2 = sloped ? size : -size;
1140 v3f(-size, y2 + offset, size),
1141 v3f( size, y2 + offset, size),
1142 v3f( size, -size + offset, -size),
1143 v3f(-size, -size + offset, -size),
1146 for (int i = 0; i < 4; i++)
1147 vertices[i].rotateXZBy(angle);
1151 void MapblockMeshGenerator::drawNodeboxNode()
1153 static const v3s16 tile_dirs[6] = {
1162 // we have this order for some reason...
1163 static const v3s16 connection_dirs[6] = {
1164 v3s16( 0, 1, 0), // top
1165 v3s16( 0, -1, 0), // bottom
1166 v3s16( 0, 0, -1), // front
1167 v3s16(-1, 0, 0), // left
1168 v3s16( 0, 0, 1), // back
1169 v3s16( 1, 0, 0), // right
1173 for (int face = 0; face < 6; face++) {
1174 // Handles facedir rotation for textures
1175 getTile(tile_dirs[face], tiles[face]);
1178 // locate possible neighboring nodes to connect to
1179 int neighbors_set = 0;
1180 if (f->node_box.type == NODEBOX_CONNECTED) {
1181 for (int dir = 0; dir != 6; dir++) {
1182 int flag = 1 << dir;
1183 v3s16 p2 = blockpos_nodes + p + connection_dirs[dir];
1184 MapNode n2 = data->m_vmanip.getNodeNoEx(p2);
1185 if (nodedef->nodeboxConnects(n, n2, flag))
1186 neighbors_set |= flag;
1190 std::vector<aabb3f> boxes;
1191 n.getNodeBoxes(nodedef, &boxes, neighbors_set);
1192 for (std::vector<aabb3f>::iterator i = boxes.begin(); i != boxes.end(); ++i)
1193 drawAutoLightedCuboid(*i, NULL, tiles, 6);
1196 void MapblockMeshGenerator::drawMeshNode()
1200 bool private_mesh; // as a grab/drop pair is not thread-safe
1202 if (f->param_type_2 == CPT2_FACEDIR ||
1203 f->param_type_2 == CPT2_COLORED_FACEDIR) {
1204 facedir = n.getFaceDir(nodedef);
1205 } else if (f->param_type_2 == CPT2_WALLMOUNTED ||
1206 f->param_type_2 == CPT2_COLORED_WALLMOUNTED) {
1207 // Convert wallmounted to 6dfacedir.
1208 // When cache enabled, it is already converted.
1209 facedir = n.getWallMounted(nodedef);
1210 if (!enable_mesh_cache) {
1211 static const u8 wm_to_6d[6] = {20, 0, 16 + 1, 12 + 3, 8, 4 + 2};
1212 facedir = wm_to_6d[facedir];
1216 if (!data->m_smooth_lighting && f->mesh_ptr[facedir]) {
1217 // use cached meshes
1218 private_mesh = false;
1219 mesh = f->mesh_ptr[facedir];
1220 } else if (f->mesh_ptr[0]) {
1221 // no cache, clone and rotate mesh
1222 private_mesh = true;
1223 mesh = cloneMesh(f->mesh_ptr[0]);
1224 rotateMeshBy6dFacedir(mesh, facedir);
1225 recalculateBoundingBox(mesh);
1226 meshmanip->recalculateNormals(mesh, true, false);
1230 int mesh_buffer_count = mesh->getMeshBufferCount();
1231 for (int j = 0; j < mesh_buffer_count; j++) {
1233 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1234 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1235 int vertex_count = buf->getVertexCount();
1237 if (data->m_smooth_lighting) {
1238 // Mesh is always private here. So the lighting is applied to each
1239 // vertex right here.
1240 for (int k = 0; k < vertex_count; k++) {
1241 video::S3DVertex &vertex = vertices[k];
1242 vertex.Color = blendLightColor(vertex.Pos, vertex.Normal);
1243 vertex.Pos += origin;
1245 collector->append(tile, vertices, vertex_count,
1246 buf->getIndices(), buf->getIndexCount());
1248 // Don't modify the mesh, it may not be private here.
1249 // Instead, let the collector process colors, etc.
1250 collector->append(tile, vertices, vertex_count,
1251 buf->getIndices(), buf->getIndexCount(), origin,
1252 color, f->light_source);
1259 // also called when the drawtype is known but should have been pre-converted
1260 void MapblockMeshGenerator::errorUnknownDrawtype()
1262 infostream << "Got drawtype " << f->drawtype << std::endl;
1263 FATAL_ERROR("Unknown drawtype");
1266 void MapblockMeshGenerator::drawNode()
1268 if (data->m_smooth_lighting)
1269 getSmoothLightFrame();
1271 light = getInteriorLight(n, 1, nodedef);
1272 switch (f->drawtype) {
1273 case NDT_FLOWINGLIQUID: drawLiquidNode(); break;
1274 case NDT_GLASSLIKE: drawGlasslikeNode(); break;
1275 case NDT_GLASSLIKE_FRAMED: drawGlasslikeFramedNode(); break;
1276 case NDT_ALLFACES: drawAllfacesNode(); break;
1277 case NDT_TORCHLIKE: drawTorchlikeNode(); break;
1278 case NDT_SIGNLIKE: drawSignlikeNode(); break;
1279 case NDT_PLANTLIKE: drawPlantlikeNode(); break;
1280 case NDT_FIRELIKE: drawFirelikeNode(); break;
1281 case NDT_FENCELIKE: drawFencelikeNode(); break;
1282 case NDT_RAILLIKE: drawRaillikeNode(); break;
1283 case NDT_NODEBOX: drawNodeboxNode(); break;
1284 case NDT_MESH: drawMeshNode(); break;
1285 default: errorUnknownDrawtype(); break;
1290 TODO: Fix alpha blending for special nodes
1291 Currently only the last element rendered is blended correct
1293 void MapblockMeshGenerator::generate()
1295 for (p.Z = 0; p.Z < MAP_BLOCKSIZE; p.Z++)
1296 for (p.Y = 0; p.Y < MAP_BLOCKSIZE; p.Y++)
1297 for (p.X = 0; p.X < MAP_BLOCKSIZE; p.X++) {
1298 n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1299 f = &nodedef->get(n);
1300 // Solid nodes are drawn by MapBlockMesh
1301 if (f->solidness != 0)
1303 if (f->drawtype == NDT_AIRLIKE)
1305 origin = intToFloat(p, BS);