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>
33 // Distance of light extrapolation (for oversized nodes)
34 // After this distance, it gives up and considers light level constant
35 #define SMOOTH_LIGHTING_OVERSIZE 1.0
37 // Node edge count (for glasslike-framed)
38 #define FRAMED_EDGE_COUNT 12
40 // Node neighbor count, including edge-connected, but not vertex-connected
41 // (for glasslike-framed)
42 // Corresponding offsets are listed in g_27dirs
43 #define FRAMED_NEIGHBOR_COUNT 18
45 static const v3s16 light_dirs[8] = {
56 // Standard index set to make a quad on 4 vertices
57 static constexpr u16 quad_indices[] = {0, 1, 2, 2, 3, 0};
59 const std::string MapblockMeshGenerator::raillike_groupname = "connect_to_raillike";
61 MapblockMeshGenerator::MapblockMeshGenerator(MeshMakeData *input, MeshCollector *output)
66 nodedef = data->m_client->ndef();
67 smgr = data->m_client->getSceneManager();
68 meshmanip = smgr->getMeshManipulator();
70 enable_mesh_cache = g_settings->getBool("enable_mesh_cache") &&
71 !data->m_smooth_lighting; // Mesh cache is not supported with smooth lighting
73 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
76 void MapblockMeshGenerator::useTile(int index, bool disable_backface_culling)
78 getNodeTileN(n, p, index, data, tile);
79 if (!data->m_smooth_lighting)
80 color = encode_light(light, f->light_source);
81 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
82 tile.layers[layer].material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
83 if (disable_backface_culling)
84 tile.layers[layer].material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
88 void MapblockMeshGenerator::useDefaultTile(bool set_color)
90 getNodeTile(n, p, v3s16(0, 0, 0), data, tile);
91 if (set_color && !data->m_smooth_lighting)
92 color = encode_light(light, f->light_source);
95 void MapblockMeshGenerator::getTile(const v3s16& direction, TileSpec &tile)
97 getNodeTile(n, p, direction, data, tile);
100 void MapblockMeshGenerator::drawQuad(v3f *coords, const v3s16 &normal)
102 static const v2f tcoords[4] = {v2f(0, 0), v2f(1, 0), v2f(1, 1), v2f(0, 1)};
103 video::S3DVertex vertices[4];
104 bool shade_face = !f->light_source && (normal != v3s16(0, 0, 0));
105 v3f normal2(normal.X, normal.Y, normal.Z);
106 for (int j = 0; j < 4; j++) {
107 vertices[j].Pos = coords[j] + origin;
108 vertices[j].Normal = normal2;
109 if (data->m_smooth_lighting)
110 vertices[j].Color = blendLightColor(coords[j]);
112 vertices[j].Color = color;
114 applyFacesShading(vertices[j].Color, normal2);
115 vertices[j].TCoords = tcoords[j];
117 collector->append(tile, vertices, 4, quad_indices, 6);
121 // tiles - the tiles (materials) to use (for all 6 faces)
122 // tilecount - number of entries in tiles, 1<=tilecount<=6
123 // lights - vertex light levels. The order is the same as in light_dirs.
124 // NULL may be passed if smooth lighting is disabled.
125 // txc - texture coordinates - this is a list of texture coordinates
126 // for the opposite corners of each face - therefore, there
127 // should be (2+2)*6=24 values in the list. The order of
128 // the faces in the list is up-down-right-left-back-front
129 // (compatible with ContentFeatures).
130 void MapblockMeshGenerator::drawCuboid(const aabb3f &box,
131 TileSpec *tiles, int tilecount, const u16 *lights, const f32 *txc)
133 assert(tilecount >= 1 && tilecount <= 6); // pre-condition
135 v3f min = box.MinEdge;
136 v3f max = box.MaxEdge;
138 video::SColor colors[6];
139 if (!data->m_smooth_lighting) {
140 for (int face = 0; face != 6; ++face) {
141 colors[face] = encode_light(light, 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 vertices[j].Color = encode_light(lights[light_indices[j]],
245 if (!f->light_source)
246 applyFacesShading(vertices[j].Color, vertices[j].Normal);
250 // Add to mesh collector
251 for (int k = 0; k < 6; ++k) {
252 int tileindex = MYMIN(k, tilecount - 1);
253 collector->append(tiles[tileindex], vertices + 4 * k, 4, quad_indices, 6);
257 // Gets the base lighting values for a node
258 void MapblockMeshGenerator::getSmoothLightFrame()
260 for (int k = 0; k < 8; ++k) {
261 u16 light = getSmoothLight(blockpos_nodes + p, light_dirs[k], data);
262 frame.lightsA[k] = light & 0xff;
263 frame.lightsB[k] = light >> 8;
267 // Calculates vertex light level
268 // vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
269 u16 MapblockMeshGenerator::blendLight(const v3f &vertex_pos)
271 f32 x = core::clamp(vertex_pos.X / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
272 f32 y = core::clamp(vertex_pos.Y / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
273 f32 z = core::clamp(vertex_pos.Z / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
276 for (int k = 0; k < 8; ++k) {
277 f32 dx = (k & 4) ? x : 1 - x;
278 f32 dy = (k & 2) ? y : 1 - y;
279 f32 dz = (k & 1) ? z : 1 - z;
280 lightA += dx * dy * dz * frame.lightsA[k];
281 lightB += dx * dy * dz * frame.lightsB[k];
284 core::clamp(core::round32(lightA), 0, 255) |
285 core::clamp(core::round32(lightB), 0, 255) << 8;
288 // Calculates vertex color to be used in mapblock mesh
289 // vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
290 // tile_color - node's tile color
291 video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos)
293 u16 light = blendLight(vertex_pos);
294 return encode_light(light, f->light_source);
297 video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos,
298 const v3f &vertex_normal)
300 video::SColor color = blendLightColor(vertex_pos);
301 if (!f->light_source)
302 applyFacesShading(color, vertex_normal);
306 void MapblockMeshGenerator::generateCuboidTextureCoords(const aabb3f &box, f32 *coords)
308 f32 tx1 = (box.MinEdge.X / BS) + 0.5;
309 f32 ty1 = (box.MinEdge.Y / BS) + 0.5;
310 f32 tz1 = (box.MinEdge.Z / BS) + 0.5;
311 f32 tx2 = (box.MaxEdge.X / BS) + 0.5;
312 f32 ty2 = (box.MaxEdge.Y / BS) + 0.5;
313 f32 tz2 = (box.MaxEdge.Z / BS) + 0.5;
315 tx1, 1 - tz2, tx2, 1 - tz1, // up
316 tx1, tz1, tx2, tz2, // down
317 tz1, 1 - ty2, tz2, 1 - ty1, // right
318 1 - tz2, 1 - ty2, 1 - tz1, 1 - ty1, // left
319 1 - tx2, 1 - ty2, 1 - tx1, 1 - ty1, // back
320 tx1, 1 - ty2, tx2, 1 - ty1, // front
322 for (int i = 0; i != 24; ++i)
326 void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc,
327 TileSpec *tiles, int tile_count)
329 f32 texture_coord_buf[24];
330 f32 dx1 = box.MinEdge.X;
331 f32 dy1 = box.MinEdge.Y;
332 f32 dz1 = box.MinEdge.Z;
333 f32 dx2 = box.MaxEdge.X;
334 f32 dy2 = box.MaxEdge.Y;
335 f32 dz2 = box.MaxEdge.Z;
336 box.MinEdge += origin;
337 box.MaxEdge += origin;
339 generateCuboidTextureCoords(box, texture_coord_buf);
340 txc = texture_coord_buf;
346 if (data->m_smooth_lighting) {
348 for (int j = 0; j < 8; ++j) {
350 d.X = (j & 4) ? dx2 : dx1;
351 d.Y = (j & 2) ? dy2 : dy1;
352 d.Z = (j & 1) ? dz2 : dz1;
353 lights[j] = blendLight(d);
355 drawCuboid(box, tiles, tile_count, lights, txc);
357 drawCuboid(box, tiles, tile_count, NULL, txc);
362 * Returns the i-th special tile for a map node.
364 static TileSpec getSpecialTile(const ContentFeatures &f,
365 const MapNode &n, u8 i)
367 TileSpec copy = f.special_tiles[i];
368 for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
369 TileLayer *layer = ©.layers[layernum];
370 if (layer->texture_id == 0)
372 if (!layer->has_color)
373 n.getColor(f, &(layer->color));
378 void MapblockMeshGenerator::prepareLiquidNodeDrawing()
380 tile_liquid_top = getSpecialTile(*f, n, 0);
381 tile_liquid = getSpecialTile(*f, n, 1);
383 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y + 1, p.Z));
384 c_flowing = nodedef->getId(f->liquid_alternative_flowing);
385 c_source = nodedef->getId(f->liquid_alternative_source);
386 top_is_same_liquid = (ntop.getContent() == c_flowing) || (ntop.getContent() == c_source);
388 if (data->m_smooth_lighting)
389 return; // don't need to pre-compute anything in this case
391 if (f->light_source != 0) {
392 // If this liquid emits light and doesn't contain light, draw
393 // it at what it emits, for an increased effect
394 light = decode_light(f->light_source);
395 light = light | (light << 8);
396 } else if (nodedef->get(ntop).param_type == CPT_LIGHT) {
397 // Otherwise, use the light of the node on top if possible
398 light = getInteriorLight(ntop, 0, nodedef);
401 color_liquid_top = encode_light(light, f->light_source);
402 color = encode_light(light, f->light_source);
405 void MapblockMeshGenerator::getLiquidNeighborhood()
407 u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8);
409 for (int w = -1; w <= 1; w++)
410 for (int u = -1; u <= 1; u++) {
411 NeighborData &neighbor = liquid_neighbors[w + 1][u + 1];
412 v3s16 p2 = p + v3s16(u, 0, w);
413 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
414 neighbor.content = n2.getContent();
415 neighbor.level = -0.5 * BS;
416 neighbor.is_same_liquid = false;
417 neighbor.top_is_same_liquid = false;
419 if (neighbor.content == CONTENT_IGNORE)
422 if (neighbor.content == c_source) {
423 neighbor.is_same_liquid = true;
424 neighbor.level = 0.5 * BS;
425 } else if (neighbor.content == c_flowing) {
426 neighbor.is_same_liquid = true;
427 u8 liquid_level = (n2.param2 & LIQUID_LEVEL_MASK);
428 if (liquid_level <= LIQUID_LEVEL_MAX + 1 - range)
431 liquid_level -= (LIQUID_LEVEL_MAX + 1 - range);
432 neighbor.level = (-0.5 + (liquid_level + 0.5) / range) * BS;
435 // Check node above neighbor.
436 // NOTE: This doesn't get executed if neighbor
439 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
440 if (n2.getContent() == c_source || n2.getContent() == c_flowing)
441 neighbor.top_is_same_liquid = true;
445 void MapblockMeshGenerator::calculateCornerLevels()
447 for (int k = 0; k < 2; k++)
448 for (int i = 0; i < 2; i++)
449 corner_levels[k][i] = getCornerLevel(i, k);
452 f32 MapblockMeshGenerator::getCornerLevel(int i, int k)
457 for (int dk = 0; dk < 2; dk++)
458 for (int di = 0; di < 2; di++) {
459 NeighborData &neighbor_data = liquid_neighbors[k + dk][i + di];
460 content_t content = neighbor_data.content;
462 // If top is liquid, draw starting from top of node
463 if (neighbor_data.top_is_same_liquid)
466 // Source always has the full height
467 if (content == c_source)
470 // Flowing liquid has level information
471 if (content == c_flowing) {
472 sum += neighbor_data.level;
474 } else if (content == CONTENT_AIR) {
477 return -0.5 * BS + 0.2;
485 void MapblockMeshGenerator::drawLiquidSides()
487 struct LiquidFaceDesc {
489 v3s16 p[2]; // XZ only; 1 means +, 0 means -
494 static const LiquidFaceDesc base_faces[4] = {
495 {v3s16( 1, 0, 0), {v3s16(1, 0, 1), v3s16(1, 0, 0)}},
496 {v3s16(-1, 0, 0), {v3s16(0, 0, 0), v3s16(0, 0, 1)}},
497 {v3s16( 0, 0, 1), {v3s16(0, 0, 1), v3s16(1, 0, 1)}},
498 {v3s16( 0, 0, -1), {v3s16(1, 0, 0), v3s16(0, 0, 0)}},
500 static const UV base_vertices[4] = {
506 for (int i = 0; i < 4; i++) {
507 const LiquidFaceDesc &face = base_faces[i];
508 const NeighborData &neighbor = liquid_neighbors[face.dir.Z + 1][face.dir.X + 1];
510 // No face between nodes of the same liquid, unless there is node
511 // at the top to which it should be connected. Again, unless the face
512 // there would be inside the liquid
513 if (neighbor.is_same_liquid) {
514 if (!top_is_same_liquid)
516 if (neighbor.top_is_same_liquid)
520 const ContentFeatures &neighbor_features = nodedef->get(neighbor.content);
521 // Don't draw face if neighbor is blocking the view
522 if (neighbor_features.solidness == 2)
525 video::S3DVertex vertices[4];
526 for (int j = 0; j < 4; j++) {
527 const UV &vertex = base_vertices[j];
528 const v3s16 &base = face.p[vertex.u];
530 pos.X = (base.X - 0.5) * BS;
531 pos.Z = (base.Z - 0.5) * BS;
533 pos.Y = neighbor.is_same_liquid ? corner_levels[base.Z][base.X] : -0.5 * BS;
535 pos.Y = !top_is_same_liquid ? corner_levels[base.Z][base.X] : 0.5 * BS;
536 if (data->m_smooth_lighting)
537 color = blendLightColor(pos);
539 vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, color, vertex.u, vertex.v);
541 collector->append(tile_liquid, vertices, 4, quad_indices, 6);
545 void MapblockMeshGenerator::drawLiquidTop()
547 // To get backface culling right, the vertices need to go
548 // clockwise around the front of the face. And we happened to
549 // calculate corner levels in exact reverse order.
550 static const int corner_resolve[4][2] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
552 video::S3DVertex vertices[4] = {
553 video::S3DVertex(-BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 0, 1),
554 video::S3DVertex( BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 1, 1),
555 video::S3DVertex( BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 1, 0),
556 video::S3DVertex(-BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 0, 0),
559 for (int i = 0; i < 4; i++) {
560 int u = corner_resolve[i][0];
561 int w = corner_resolve[i][1];
562 vertices[i].Pos.Y += corner_levels[w][u];
563 if (data->m_smooth_lighting)
564 vertices[i].Color = blendLightColor(vertices[i].Pos);
565 vertices[i].Pos += origin;
568 // Default downwards-flowing texture animation goes from
569 // -Z towards +Z, thus the direction is +Z.
570 // Rotate texture to make animation go in flow direction
571 // Positive if liquid moves towards +Z
572 f32 dz = (corner_levels[0][0] + corner_levels[0][1]) -
573 (corner_levels[1][0] + corner_levels[1][1]);
574 // Positive if liquid moves towards +X
575 f32 dx = (corner_levels[0][0] + corner_levels[1][0]) -
576 (corner_levels[0][1] + corner_levels[1][1]);
577 f32 tcoord_angle = atan2(dz, dx) * core::RADTODEG;
578 v2f tcoord_center(0.5, 0.5);
579 v2f tcoord_translate(blockpos_nodes.Z + p.Z, blockpos_nodes.X + p.X);
580 tcoord_translate.rotateBy(tcoord_angle);
581 tcoord_translate.X -= floor(tcoord_translate.X);
582 tcoord_translate.Y -= floor(tcoord_translate.Y);
584 for (int i = 0; i < 4; i++) {
585 vertices[i].TCoords.rotateBy(tcoord_angle, tcoord_center);
586 vertices[i].TCoords += tcoord_translate;
589 std::swap(vertices[0].TCoords, vertices[2].TCoords);
591 collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
594 void MapblockMeshGenerator::drawLiquidNode()
596 prepareLiquidNodeDrawing();
597 getLiquidNeighborhood();
598 calculateCornerLevels();
600 if (!top_is_same_liquid)
604 void MapblockMeshGenerator::drawGlasslikeNode()
608 for (int face = 0; face < 6; face++) {
609 // Check this neighbor
610 v3s16 dir = g_6dirs[face];
611 v3s16 neighbor_pos = blockpos_nodes + p + dir;
612 MapNode neighbor = data->m_vmanip.getNodeNoExNoEmerge(neighbor_pos);
613 // Don't make face if neighbor is of same type
614 if (neighbor.getContent() == n.getContent())
618 v3f(-BS / 2, BS / 2, -BS / 2),
619 v3f( BS / 2, BS / 2, -BS / 2),
620 v3f( BS / 2, -BS / 2, -BS / 2),
621 v3f(-BS / 2, -BS / 2, -BS / 2),
623 for (int i = 0; i < 4; i++) {
625 case D6D_ZP: vertices[i].rotateXZBy(180); break;
626 case D6D_YP: vertices[i].rotateYZBy( 90); break;
627 case D6D_XP: vertices[i].rotateXZBy( 90); break;
628 case D6D_ZN: vertices[i].rotateXZBy( 0); break;
629 case D6D_YN: vertices[i].rotateYZBy(-90); break;
630 case D6D_XN: vertices[i].rotateXZBy(-90); break;
633 drawQuad(vertices, dir);
637 void MapblockMeshGenerator::drawGlasslikeFramedNode()
640 for (int face = 0; face < 6; face++)
641 getTile(g_6dirs[face], tiles[face]);
643 TileSpec glass_tiles[6];
644 if (tiles[1].layers[0].texture &&
645 tiles[2].layers[0].texture &&
646 tiles[3].layers[0].texture) {
647 glass_tiles[0] = tiles[4];
648 glass_tiles[1] = tiles[0];
649 glass_tiles[2] = tiles[4];
650 glass_tiles[3] = tiles[4];
651 glass_tiles[4] = tiles[3];
652 glass_tiles[5] = tiles[4];
654 for (int face = 0; face < 6; face++)
655 glass_tiles[face] = tiles[4];
658 u8 param2 = n.getParam2();
659 bool H_merge = !(param2 & 128);
660 bool V_merge = !(param2 & 64);
663 static const float a = BS / 2;
664 static const float g = a - 0.003;
665 static const float b = .876 * ( BS / 2 );
667 static const aabb3f frame_edges[FRAMED_EDGE_COUNT] = {
668 aabb3f( b, b, -a, a, a, a), // y+
669 aabb3f(-a, b, -a, -b, a, a), // y+
670 aabb3f( b, -a, -a, a, -b, a), // y-
671 aabb3f(-a, -a, -a, -b, -b, a), // y-
672 aabb3f( b, -a, b, a, a, a), // x+
673 aabb3f( b, -a, -a, a, a, -b), // x+
674 aabb3f(-a, -a, b, -b, a, a), // x-
675 aabb3f(-a, -a, -a, -b, a, -b), // x-
676 aabb3f(-a, b, b, a, a, a), // z+
677 aabb3f(-a, -a, b, a, -b, a), // z+
678 aabb3f(-a, -a, -a, a, -b, -b), // z-
679 aabb3f(-a, b, -a, a, a, -b), // z-
681 static const aabb3f glass_faces[6] = {
682 aabb3f(-g, -g, g, g, g, g), // z+
683 aabb3f(-g, g, -g, g, g, g), // y+
684 aabb3f( g, -g, -g, g, g, g), // x+
685 aabb3f(-g, -g, -g, g, g, -g), // z-
686 aabb3f(-g, -g, -g, g, -g, g), // y-
687 aabb3f(-g, -g, -g, -g, g, g), // x-
690 // tables of neighbour (connect if same type and merge allowed),
691 // checked with g_26dirs
693 // 1 = connect, 0 = face visible
694 bool nb[FRAMED_NEIGHBOR_COUNT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
697 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};
698 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};
699 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};
700 const bool *check_nb = check_nb_all;
702 // neighbours checks for frames visibility
703 if (H_merge || V_merge) {
705 check_nb = check_nb_vertical; // vertical-only merge
707 check_nb = check_nb_horizontal; // horizontal-only merge
708 content_t current = n.getContent();
709 for (int i = 0; i < FRAMED_NEIGHBOR_COUNT; i++) {
712 v3s16 n2p = blockpos_nodes + p + g_26dirs[i];
713 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
714 content_t n2c = n2.getContent();
715 if (n2c == current || n2c == CONTENT_IGNORE)
722 static const u8 nb_triplet[FRAMED_EDGE_COUNT][3] = {
723 {1, 2, 7}, {1, 5, 6}, {4, 2, 15}, {4, 5, 14},
724 {2, 0, 11}, {2, 3, 13}, {5, 0, 10}, {5, 3, 12},
725 {0, 1, 8}, {0, 4, 16}, {3, 4, 17}, {3, 1, 9},
729 for (int edge = 0; edge < FRAMED_EDGE_COUNT; edge++) {
731 if (nb[nb_triplet[edge][2]])
732 edge_invisible = nb[nb_triplet[edge][0]] & nb[nb_triplet[edge][1]];
734 edge_invisible = nb[nb_triplet[edge][0]] ^ nb[nb_triplet[edge][1]];
737 drawAutoLightedCuboid(frame_edges[edge]);
740 for (int face = 0; face < 6; face++) {
743 tile = glass_tiles[face];
744 drawAutoLightedCuboid(glass_faces[face]);
747 // Optionally render internal liquid level defined by param2
748 // Liquid is textured with 1 tile defined in nodedef 'special_tiles'
749 if (param2 > 0 && f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL &&
750 f->special_tiles[0].layers[0].texture) {
751 // Internal liquid level has param2 range 0 .. 63,
752 // convert it to -0.5 .. 0.5
753 float vlev = (param2 / 63.0) * 2.0 - 1.0;
754 tile = getSpecialTile(*f, n, 0);
755 drawAutoLightedCuboid(aabb3f(-(nb[5] ? g : b),
759 (nb[1] ? g : b) * vlev,
764 void MapblockMeshGenerator::drawAllfacesNode()
766 static const aabb3f box(-BS / 2, -BS / 2, -BS / 2, BS / 2, BS / 2, BS / 2);
767 useDefaultTile(false);
768 drawAutoLightedCuboid(box);
771 void MapblockMeshGenerator::drawTorchlikeNode()
773 u8 wall = n.getWallMounted(nodedef);
776 case DWM_YP: tileindex = 1; break; // ceiling
777 case DWM_YN: tileindex = 0; break; // floor
778 default: tileindex = 2; // side (or invalid—should we care?)
780 useTile(tileindex, true);
782 float size = BS / 2 * f->visual_scale;
786 v3f( size, -size, 0),
787 v3f(-size, -size, 0),
789 for (int i = 0; i < 4; i++) {
791 case DWM_YP: vertices[i].rotateXZBy(-45); break;
792 case DWM_YN: vertices[i].rotateXZBy( 45); break;
793 case DWM_XP: vertices[i].rotateXZBy( 0); break;
794 case DWM_XN: vertices[i].rotateXZBy(180); break;
795 case DWM_ZP: vertices[i].rotateXZBy( 90); break;
796 case DWM_ZN: vertices[i].rotateXZBy(-90); break;
802 void MapblockMeshGenerator::drawSignlikeNode()
804 u8 wall = n.getWallMounted(nodedef);
806 static const float offset = BS / 16;
807 float size = BS / 2 * f->visual_scale;
808 // Wall at X+ of node
810 v3f(BS / 2 - offset, size, size),
811 v3f(BS / 2 - offset, size, -size),
812 v3f(BS / 2 - offset, -size, -size),
813 v3f(BS / 2 - offset, -size, size),
815 for (int i = 0; i < 4; i++) {
817 case DWM_YP: vertices[i].rotateXYBy( 90); break;
818 case DWM_YN: vertices[i].rotateXYBy(-90); break;
819 case DWM_XP: vertices[i].rotateXZBy( 0); break;
820 case DWM_XN: vertices[i].rotateXZBy(180); break;
821 case DWM_ZP: vertices[i].rotateXZBy( 90); break;
822 case DWM_ZN: vertices[i].rotateXZBy(-90); break;
828 void MapblockMeshGenerator::drawPlantlikeQuad(float rotation, float quad_offset,
829 bool offset_top_only)
832 v3f(-scale, -BS / 2 + scale * 2, 0),
833 v3f( scale, -BS / 2 + scale * 2, 0),
834 v3f( scale, -BS / 2, 0),
835 v3f(-scale, -BS / 2, 0),
837 if (random_offset_Y) {
838 PseudoRandom yrng(face_num++ | p.X << 16 | p.Z << 8 | p.Y << 24);
839 offset.Y = -BS * ((yrng.next() % 16 / 16.0) * 0.125);
841 int offset_count = offset_top_only ? 2 : 4;
842 for (int i = 0; i < offset_count; i++)
843 vertices[i].Z += quad_offset;
844 for (int i = 0; i < 4; i++) {
845 vertices[i].rotateXZBy(rotation + rotate_degree);
846 vertices[i] += offset;
851 void MapblockMeshGenerator::drawPlantlikeNode()
854 draw_style = PLANT_STYLE_CROSS;
855 scale = BS / 2 * f->visual_scale;
856 offset = v3f(0, 0, 0);
858 random_offset_Y = false;
861 switch (f->param_type_2) {
862 case CPT2_MESHOPTIONS:
863 draw_style = PlantlikeStyle(n.param2 & MO_MASK_STYLE);
864 if (n.param2 & MO_BIT_SCALE_SQRT2)
866 if (n.param2 & MO_BIT_RANDOM_OFFSET) {
867 PseudoRandom rng(p.X << 8 | p.Z | p.Y << 16);
868 offset.X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
869 offset.Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
871 if (n.param2 & MO_BIT_RANDOM_OFFSET_Y)
872 random_offset_Y = true;
876 rotate_degree = n.param2 * 2;
883 switch (draw_style) {
884 case PLANT_STYLE_CROSS:
885 drawPlantlikeQuad(46);
886 drawPlantlikeQuad(-44);
889 case PLANT_STYLE_CROSS2:
890 drawPlantlikeQuad(91);
891 drawPlantlikeQuad(1);
894 case PLANT_STYLE_STAR:
895 drawPlantlikeQuad(121);
896 drawPlantlikeQuad(241);
897 drawPlantlikeQuad(1);
900 case PLANT_STYLE_HASH:
901 drawPlantlikeQuad( 1, BS / 4);
902 drawPlantlikeQuad( 91, BS / 4);
903 drawPlantlikeQuad(181, BS / 4);
904 drawPlantlikeQuad(271, BS / 4);
907 case PLANT_STYLE_HASH2:
908 drawPlantlikeQuad( 1, -BS / 2, true);
909 drawPlantlikeQuad( 91, -BS / 2, true);
910 drawPlantlikeQuad(181, -BS / 2, true);
911 drawPlantlikeQuad(271, -BS / 2, true);
916 void MapblockMeshGenerator::drawFirelikeQuad(float rotation, float opening_angle,
917 float offset_h, float offset_v)
920 v3f(-scale, -BS / 2 + scale * 2, 0),
921 v3f( scale, -BS / 2 + scale * 2, 0),
922 v3f( scale, -BS / 2, 0),
923 v3f(-scale, -BS / 2, 0),
925 for (int i = 0; i < 4; i++) {
926 vertices[i].rotateYZBy(opening_angle);
927 vertices[i].Z += offset_h;
928 vertices[i].rotateXZBy(rotation);
929 vertices[i].Y += offset_v;
934 void MapblockMeshGenerator::drawFirelikeNode()
937 scale = BS / 2 * f->visual_scale;
939 // Check for adjacent nodes
940 bool neighbors = false;
941 bool neighbor[6] = {0, 0, 0, 0, 0, 0};
942 content_t current = n.getContent();
943 for (int i = 0; i < 6; i++) {
944 v3s16 n2p = blockpos_nodes + p + g_6dirs[i];
945 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
946 content_t n2c = n2.getContent();
947 if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
952 bool drawBasicFire = neighbor[D6D_YN] || !neighbors;
953 bool drawBottomFire = neighbor[D6D_YP];
955 if (drawBasicFire || neighbor[D6D_ZP])
956 drawFirelikeQuad(0, -10, 0.4 * BS);
957 else if (drawBottomFire)
958 drawFirelikeQuad(0, 70, 0.47 * BS, 0.484 * BS);
960 if (drawBasicFire || neighbor[D6D_XN])
961 drawFirelikeQuad(90, -10, 0.4 * BS);
962 else if (drawBottomFire)
963 drawFirelikeQuad(90, 70, 0.47 * BS, 0.484 * BS);
965 if (drawBasicFire || neighbor[D6D_ZN])
966 drawFirelikeQuad(180, -10, 0.4 * BS);
967 else if (drawBottomFire)
968 drawFirelikeQuad(180, 70, 0.47 * BS, 0.484 * BS);
970 if (drawBasicFire || neighbor[D6D_XP])
971 drawFirelikeQuad(270, -10, 0.4 * BS);
972 else if (drawBottomFire)
973 drawFirelikeQuad(270, 70, 0.47 * BS, 0.484 * BS);
976 drawFirelikeQuad(45, 0, 0.0);
977 drawFirelikeQuad(-45, 0, 0.0);
981 void MapblockMeshGenerator::drawFencelikeNode()
983 useDefaultTile(false);
984 TileSpec tile_nocrack = tile;
985 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++)
986 tile_nocrack.layers[layer].material_flags &= ~MATERIAL_FLAG_CRACK;
988 // Put wood the right way around in the posts
989 TileSpec tile_rot = tile;
990 tile_rot.rotation = 1;
992 static const f32 post_rad = BS / 8;
993 static const f32 bar_rad = BS / 16;
994 static const f32 bar_len = BS / 2 - post_rad;
996 // The post - always present
997 static const aabb3f post(-post_rad, -BS / 2, -post_rad,
998 post_rad, BS / 2, post_rad);
999 static const f32 postuv[24] = {
1000 0.375, 0.375, 0.625, 0.625,
1001 0.375, 0.375, 0.625, 0.625,
1002 0.000, 0.000, 0.250, 1.000,
1003 0.250, 0.000, 0.500, 1.000,
1004 0.500, 0.000, 0.750, 1.000,
1005 0.750, 0.000, 1.000, 1.000,
1008 drawAutoLightedCuboid(post, postuv);
1010 tile = tile_nocrack;
1012 // Now a section of fence, +X, if there's a post there
1015 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1016 const ContentFeatures *f2 = &nodedef->get(n2);
1017 if (f2->drawtype == NDT_FENCELIKE) {
1018 static const aabb3f bar_x1(BS / 2 - bar_len, BS / 4 - bar_rad, -bar_rad,
1019 BS / 2 + bar_len, BS / 4 + bar_rad, bar_rad);
1020 static const aabb3f bar_x2(BS / 2 - bar_len, -BS / 4 - bar_rad, -bar_rad,
1021 BS / 2 + bar_len, -BS / 4 + bar_rad, bar_rad);
1022 static const f32 xrailuv[24] = {
1023 0.000, 0.125, 1.000, 0.250,
1024 0.000, 0.250, 1.000, 0.375,
1025 0.375, 0.375, 0.500, 0.500,
1026 0.625, 0.625, 0.750, 0.750,
1027 0.000, 0.500, 1.000, 0.625,
1028 0.000, 0.875, 1.000, 1.000,
1030 drawAutoLightedCuboid(bar_x1, xrailuv);
1031 drawAutoLightedCuboid(bar_x2, xrailuv);
1034 // Now a section of fence, +Z, if there's a post there
1037 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1038 f2 = &nodedef->get(n2);
1039 if (f2->drawtype == NDT_FENCELIKE) {
1040 static const aabb3f bar_z1(-bar_rad, BS / 4 - bar_rad, BS / 2 - bar_len,
1041 bar_rad, BS / 4 + bar_rad, BS / 2 + bar_len);
1042 static const aabb3f bar_z2(-bar_rad, -BS / 4 - bar_rad, BS / 2 - bar_len,
1043 bar_rad, -BS / 4 + bar_rad, BS / 2 + bar_len);
1044 static const f32 zrailuv[24] = {
1045 0.1875, 0.0625, 0.3125, 0.3125, // cannot rotate; stretch
1046 0.2500, 0.0625, 0.3750, 0.3125, // for wood texture instead
1047 0.0000, 0.5625, 1.0000, 0.6875,
1048 0.0000, 0.3750, 1.0000, 0.5000,
1049 0.3750, 0.3750, 0.5000, 0.5000,
1050 0.6250, 0.6250, 0.7500, 0.7500,
1052 drawAutoLightedCuboid(bar_z1, zrailuv);
1053 drawAutoLightedCuboid(bar_z2, zrailuv);
1057 bool MapblockMeshGenerator::isSameRail(v3s16 dir)
1059 MapNode node2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
1060 if (node2.getContent() == n.getContent())
1062 const ContentFeatures &def2 = nodedef->get(node2);
1063 return ((def2.drawtype == NDT_RAILLIKE) &&
1064 (def2.getGroup(raillike_groupname) == raillike_group));
1067 void MapblockMeshGenerator::drawRaillikeNode()
1069 static const v3s16 direction[4] = {
1075 static const int slope_angle[4] = {0, 180, 90, -90};
1087 static const RailDesc rail_kinds[16] = {
1090 {straight, 0}, // . . . .
1091 {straight, 0}, // . . . +Z
1092 {straight, 0}, // . . -Z .
1093 {straight, 0}, // . . -Z +Z
1094 {straight, 90}, // . -X . .
1095 { curved, 180}, // . -X . +Z
1096 { curved, 270}, // . -X -Z .
1097 {junction, 180}, // . -X -Z +Z
1098 {straight, 90}, // +X . . .
1099 { curved, 90}, // +X . . +Z
1100 { curved, 0}, // +X . -Z .
1101 {junction, 0}, // +X . -Z +Z
1102 {straight, 90}, // +X -X . .
1103 {junction, 90}, // +X -X . +Z
1104 {junction, 270}, // +X -X -Z .
1105 { cross, 0}, // +X -X -Z +Z
1108 raillike_group = nodedef->get(n).getGroup(raillike_groupname);
1113 bool sloped = false;
1114 for (int dir = 0; dir < 4; dir++) {
1115 bool rail_above = isSameRail(direction[dir] + v3s16(0, 1, 0));
1118 angle = slope_angle[dir];
1121 isSameRail(direction[dir]) ||
1122 isSameRail(direction[dir] + v3s16(0, -1, 0)))
1127 tile_index = straight;
1129 tile_index = rail_kinds[code].tile_index;
1130 angle = rail_kinds[code].angle;
1133 useTile(tile_index, true);
1135 static const float offset = BS / 64;
1136 static const float size = BS / 2;
1137 float y2 = sloped ? size : -size;
1139 v3f(-size, y2 + offset, size),
1140 v3f( size, y2 + offset, size),
1141 v3f( size, -size + offset, -size),
1142 v3f(-size, -size + offset, -size),
1145 for (int i = 0; i < 4; i++)
1146 vertices[i].rotateXZBy(angle);
1150 void MapblockMeshGenerator::drawNodeboxNode()
1152 static const v3s16 tile_dirs[6] = {
1161 // we have this order for some reason...
1162 static const v3s16 connection_dirs[6] = {
1163 v3s16( 0, 1, 0), // top
1164 v3s16( 0, -1, 0), // bottom
1165 v3s16( 0, 0, -1), // front
1166 v3s16(-1, 0, 0), // left
1167 v3s16( 0, 0, 1), // back
1168 v3s16( 1, 0, 0), // right
1172 for (int face = 0; face < 6; face++) {
1173 // Handles facedir rotation for textures
1174 getTile(tile_dirs[face], tiles[face]);
1177 // locate possible neighboring nodes to connect to
1178 int neighbors_set = 0;
1179 if (f->node_box.type == NODEBOX_CONNECTED) {
1180 for (int dir = 0; dir != 6; dir++) {
1181 int flag = 1 << dir;
1182 v3s16 p2 = blockpos_nodes + p + connection_dirs[dir];
1183 MapNode n2 = data->m_vmanip.getNodeNoEx(p2);
1184 if (nodedef->nodeboxConnects(n, n2, flag))
1185 neighbors_set |= flag;
1189 std::vector<aabb3f> boxes;
1190 n.getNodeBoxes(nodedef, &boxes, neighbors_set);
1191 for (std::vector<aabb3f>::iterator i = boxes.begin(); i != boxes.end(); ++i)
1192 drawAutoLightedCuboid(*i, NULL, tiles, 6);
1195 void MapblockMeshGenerator::drawMeshNode()
1199 bool private_mesh; // as a grab/drop pair is not thread-safe
1201 if (f->param_type_2 == CPT2_FACEDIR ||
1202 f->param_type_2 == CPT2_COLORED_FACEDIR) {
1203 facedir = n.getFaceDir(nodedef);
1204 } else if (f->param_type_2 == CPT2_WALLMOUNTED ||
1205 f->param_type_2 == CPT2_COLORED_WALLMOUNTED) {
1206 // Convert wallmounted to 6dfacedir.
1207 // When cache enabled, it is already converted.
1208 facedir = n.getWallMounted(nodedef);
1209 if (!enable_mesh_cache) {
1210 static const u8 wm_to_6d[6] = {20, 0, 16 + 1, 12 + 3, 8, 4 + 2};
1211 facedir = wm_to_6d[facedir];
1215 if (!data->m_smooth_lighting && f->mesh_ptr[facedir]) {
1216 // use cached meshes
1217 private_mesh = false;
1218 mesh = f->mesh_ptr[facedir];
1219 } else if (f->mesh_ptr[0]) {
1220 // no cache, clone and rotate mesh
1221 private_mesh = true;
1222 mesh = cloneMesh(f->mesh_ptr[0]);
1223 rotateMeshBy6dFacedir(mesh, facedir);
1224 recalculateBoundingBox(mesh);
1225 meshmanip->recalculateNormals(mesh, true, false);
1229 int mesh_buffer_count = mesh->getMeshBufferCount();
1230 for (int j = 0; j < mesh_buffer_count; j++) {
1232 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1233 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1234 int vertex_count = buf->getVertexCount();
1236 if (data->m_smooth_lighting) {
1237 // Mesh is always private here. So the lighting is applied to each
1238 // vertex right here.
1239 for (int k = 0; k < vertex_count; k++) {
1240 video::S3DVertex &vertex = vertices[k];
1241 vertex.Color = blendLightColor(vertex.Pos, vertex.Normal);
1242 vertex.Pos += origin;
1244 collector->append(tile, vertices, vertex_count,
1245 buf->getIndices(), buf->getIndexCount());
1247 // Don't modify the mesh, it may not be private here.
1248 // Instead, let the collector process colors, etc.
1249 collector->append(tile, vertices, vertex_count,
1250 buf->getIndices(), buf->getIndexCount(), origin,
1251 color, f->light_source);
1258 // also called when the drawtype is known but should have been pre-converted
1259 void MapblockMeshGenerator::errorUnknownDrawtype()
1261 infostream << "Got drawtype " << f->drawtype << std::endl;
1262 FATAL_ERROR("Unknown drawtype");
1265 void MapblockMeshGenerator::drawNode()
1267 if (data->m_smooth_lighting)
1268 getSmoothLightFrame();
1270 light = getInteriorLight(n, 1, nodedef);
1271 switch (f->drawtype) {
1272 case NDT_FLOWINGLIQUID: drawLiquidNode(); break;
1273 case NDT_GLASSLIKE: drawGlasslikeNode(); break;
1274 case NDT_GLASSLIKE_FRAMED: drawGlasslikeFramedNode(); break;
1275 case NDT_ALLFACES: drawAllfacesNode(); break;
1276 case NDT_TORCHLIKE: drawTorchlikeNode(); break;
1277 case NDT_SIGNLIKE: drawSignlikeNode(); break;
1278 case NDT_PLANTLIKE: drawPlantlikeNode(); break;
1279 case NDT_FIRELIKE: drawFirelikeNode(); break;
1280 case NDT_FENCELIKE: drawFencelikeNode(); break;
1281 case NDT_RAILLIKE: drawRaillikeNode(); break;
1282 case NDT_NODEBOX: drawNodeboxNode(); break;
1283 case NDT_MESH: drawMeshNode(); break;
1284 default: errorUnknownDrawtype(); break;
1289 TODO: Fix alpha blending for special nodes
1290 Currently only the last element rendered is blended correct
1292 void MapblockMeshGenerator::generate()
1294 for (p.Z = 0; p.Z < MAP_BLOCKSIZE; p.Z++)
1295 for (p.Y = 0; p.Y < MAP_BLOCKSIZE; p.Y++)
1296 for (p.X = 0; p.X < MAP_BLOCKSIZE; p.X++) {
1297 n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1298 f = &nodedef->get(n);
1299 // Solid nodes are drawn by MapBlockMesh
1300 if (f->solidness != 0)
1302 if (f->drawtype == NDT_AIRLIKE)
1304 origin = intToFloat(p, BS);