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 const 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 tile = getNodeTileN(n, p, index, data);
79 if (!data->m_smooth_lighting)
80 color = encode_light_and_color(light, tile.color, f->light_source);
81 if (disable_backface_culling)
82 tile.material_flags &= ~MATERIAL_FLAG_BACKFACE_CULLING;
83 tile.material_flags |= MATERIAL_FLAG_CRACK_OVERLAY;
86 void MapblockMeshGenerator::useDefaultTile(bool set_color)
88 tile = getNodeTile(n, p, v3s16(0, 0, 0), data);
89 if (set_color && !data->m_smooth_lighting)
90 color = encode_light_and_color(light, tile.color, f->light_source);
93 TileSpec MapblockMeshGenerator::getTile(const v3s16& direction)
95 return getNodeTile(n, p, direction, data);
98 void MapblockMeshGenerator::drawQuad(v3f *coords, const v3s16 &normal)
100 static const v2f tcoords[4] = {v2f(0, 1), v2f(1, 1), v2f(1, 0), v2f(0, 0)};
101 video::S3DVertex vertices[4];
102 bool shade_face = !f->light_source && (normal != v3s16(0, 0, 0));
103 v3f normal2(normal.X, normal.Y, normal.Z);
104 for (int j = 0; j < 4; j++) {
105 vertices[j].Pos = coords[j] + origin;
106 vertices[j].Normal = normal2;
107 if (data->m_smooth_lighting)
108 vertices[j].Color = blendLight(coords[j], tile.color);
110 vertices[j].Color = color;
112 applyFacesShading(vertices[j].Color, normal2);
113 vertices[j].TCoords = tcoords[j];
115 collector->append(tile, vertices, 4, quad_indices, 6);
119 // tiles - the tiles (materials) to use (for all 6 faces)
120 // tilecount - number of entries in tiles, 1<=tilecount<=6
121 // lights - vertex light levels. The order is the same as in light_dirs.
122 // NULL may be passed if smooth lighting is disabled.
123 // txc - texture coordinates - this is a list of texture coordinates
124 // for the opposite corners of each face - therefore, there
125 // should be (2+2)*6=24 values in the list. The order of
126 // the faces in the list is up-down-right-left-back-front
127 // (compatible with ContentFeatures).
128 void MapblockMeshGenerator::drawCuboid(const aabb3f &box,
129 TileSpec *tiles, int tilecount, const u16 *lights, const f32 *txc)
131 assert(tilecount >= 1 && tilecount <= 6); // pre-condition
133 v3f min = box.MinEdge;
134 v3f max = box.MaxEdge;
136 video::SColor colors[6];
137 if (!data->m_smooth_lighting) {
138 for (int face = 0; face != 6; ++face) {
139 int tileindex = MYMIN(face, tilecount - 1);
140 colors[face] = encode_light_and_color(light, tiles[tileindex].color, f->light_source);
142 if (!f->light_source) {
143 applyFacesShading(colors[0], v3f(0, 1, 0));
144 applyFacesShading(colors[1], v3f(0, -1, 0));
145 applyFacesShading(colors[2], v3f(1, 0, 0));
146 applyFacesShading(colors[3], v3f(-1, 0, 0));
147 applyFacesShading(colors[4], v3f(0, 0, 1));
148 applyFacesShading(colors[5], v3f(0, 0, -1));
152 video::S3DVertex vertices[24] = {
154 video::S3DVertex(min.X, max.Y, max.Z, 0, 1, 0, colors[0], txc[0], txc[1]),
155 video::S3DVertex(max.X, max.Y, max.Z, 0, 1, 0, colors[0], txc[2], txc[1]),
156 video::S3DVertex(max.X, max.Y, min.Z, 0, 1, 0, colors[0], txc[2], txc[3]),
157 video::S3DVertex(min.X, max.Y, min.Z, 0, 1, 0, colors[0], txc[0], txc[3]),
159 video::S3DVertex(min.X, min.Y, min.Z, 0, -1, 0, colors[1], txc[4], txc[5]),
160 video::S3DVertex(max.X, min.Y, min.Z, 0, -1, 0, colors[1], txc[6], txc[5]),
161 video::S3DVertex(max.X, min.Y, max.Z, 0, -1, 0, colors[1], txc[6], txc[7]),
162 video::S3DVertex(min.X, min.Y, max.Z, 0, -1, 0, colors[1], txc[4], txc[7]),
164 video::S3DVertex(max.X, max.Y, min.Z, 1, 0, 0, colors[2], txc[ 8], txc[9]),
165 video::S3DVertex(max.X, max.Y, max.Z, 1, 0, 0, colors[2], txc[10], txc[9]),
166 video::S3DVertex(max.X, min.Y, max.Z, 1, 0, 0, colors[2], txc[10], txc[11]),
167 video::S3DVertex(max.X, min.Y, min.Z, 1, 0, 0, colors[2], txc[ 8], txc[11]),
169 video::S3DVertex(min.X, max.Y, max.Z, -1, 0, 0, colors[3], txc[12], txc[13]),
170 video::S3DVertex(min.X, max.Y, min.Z, -1, 0, 0, colors[3], txc[14], txc[13]),
171 video::S3DVertex(min.X, min.Y, min.Z, -1, 0, 0, colors[3], txc[14], txc[15]),
172 video::S3DVertex(min.X, min.Y, max.Z, -1, 0, 0, colors[3], txc[12], txc[15]),
174 video::S3DVertex(max.X, max.Y, max.Z, 0, 0, 1, colors[4], txc[16], txc[17]),
175 video::S3DVertex(min.X, max.Y, max.Z, 0, 0, 1, colors[4], txc[18], txc[17]),
176 video::S3DVertex(min.X, min.Y, max.Z, 0, 0, 1, colors[4], txc[18], txc[19]),
177 video::S3DVertex(max.X, min.Y, max.Z, 0, 0, 1, colors[4], txc[16], txc[19]),
179 video::S3DVertex(min.X, max.Y, min.Z, 0, 0, -1, colors[5], txc[20], txc[21]),
180 video::S3DVertex(max.X, max.Y, min.Z, 0, 0, -1, colors[5], txc[22], txc[21]),
181 video::S3DVertex(max.X, min.Y, min.Z, 0, 0, -1, colors[5], txc[22], txc[23]),
182 video::S3DVertex(min.X, min.Y, min.Z, 0, 0, -1, colors[5], txc[20], txc[23]),
185 static const u8 light_indices[24] = {
194 for (int face = 0; face < 6; face++) {
195 int tileindex = MYMIN(face, tilecount - 1);
196 const TileSpec &tile = tiles[tileindex];
197 for (int j = 0; j < 4; j++) {
198 video::S3DVertex &vertex = vertices[face * 4 + j];
199 v2f &tcoords = vertex.TCoords;
200 switch (tile.rotation) {
204 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
207 tcoords.rotateBy(180, irr::core::vector2df(0, 0));
210 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
213 tcoords.X = 1.0 - tcoords.X;
214 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
217 tcoords.X = 1.0 - tcoords.X;
218 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
221 tcoords.Y = 1.0 - tcoords.Y;
222 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
225 tcoords.Y = 1.0 - tcoords.Y;
226 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
229 tcoords.X = 1.0 - tcoords.X;
232 tcoords.Y = 1.0 - tcoords.Y;
240 if (data->m_smooth_lighting) {
241 for (int j = 0; j < 24; ++j) {
242 int tileindex = MYMIN(j / 4, tilecount - 1);
243 vertices[j].Color = encode_light_and_color(lights[light_indices[j]],
244 tiles[tileindex].color, f->light_source);
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::blendLight(const v3f &vertex_pos,
292 video::SColor tile_color)
294 u16 light = blendLight(vertex_pos);
295 return encode_light_and_color(light, tile_color, f->light_source);
298 video::SColor MapblockMeshGenerator::blendLight(const v3f &vertex_pos,
299 const v3f &vertex_normal, video::SColor tile_color)
301 video::SColor color = blendLight(vertex_pos, tile_color);
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];
370 n.getColor(f, ©.color);
374 void MapblockMeshGenerator::prepareLiquidNodeDrawing(bool flowing)
376 tile_liquid_top = getSpecialTile(*f, n, 0);
377 tile_liquid = getSpecialTile(*f, n, flowing ? 1 : 0);
379 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y + 1, p.Z));
380 c_flowing = nodedef->getId(f->liquid_alternative_flowing);
381 c_source = nodedef->getId(f->liquid_alternative_source);
382 top_is_same_liquid = (ntop.getContent() == c_flowing) || (ntop.getContent() == c_source);
384 if (data->m_smooth_lighting)
385 return; // don't need to pre-compute anything in this case
387 if (f->light_source != 0) {
388 // If this liquid emits light and doesn't contain light, draw
389 // it at what it emits, for an increased effect
390 light = decode_light(f->light_source);
391 light = light | (light << 8);
392 } else if (nodedef->get(ntop).param_type == CPT_LIGHT) {
393 // Otherwise, use the light of the node on top if possible
394 light = getInteriorLight(ntop, 0, nodedef);
397 color_liquid_top = encode_light_and_color(light, tile_liquid_top.color, f->light_source);
398 color = encode_light_and_color(light, tile_liquid.color, f->light_source);
401 void MapblockMeshGenerator::getLiquidNeighborhood(bool flowing)
403 u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8);
405 for (int w = -1; w <= 1; w++)
406 for (int u = -1; u <= 1; u++) {
407 // Skip getting unneeded data
408 if (!flowing && u && w)
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::resetCornerLevels()
447 for (int k = 0; k < 2; k++)
448 for (int i = 0; i < 2; i++)
449 corner_levels[k][i] = 0.5 * BS;
452 void MapblockMeshGenerator::calculateCornerLevels()
454 for (int k = 0; k < 2; k++)
455 for (int i = 0; i < 2; i++)
456 corner_levels[k][i] = getCornerLevel(i, k);
459 f32 MapblockMeshGenerator::getCornerLevel(int i, int k)
464 for (int dk = 0; dk < 2; dk++)
465 for (int di = 0; di < 2; di++) {
466 NeighborData &neighbor_data = liquid_neighbors[k + dk][i + di];
467 content_t content = neighbor_data.content;
469 // If top is liquid, draw starting from top of node
470 if (neighbor_data.top_is_same_liquid)
473 // Source always has the full height
474 if (content == c_source)
477 // Flowing liquid has level information
478 if (content == c_flowing) {
479 sum += neighbor_data.level;
481 } else if (content == CONTENT_AIR) {
484 return -0.5 * BS + 0.2;
492 void MapblockMeshGenerator::drawLiquidSides(bool flowing)
494 struct LiquidFaceDesc {
496 v3s16 p[2]; // XZ only; 1 means +, 0 means -
501 static const LiquidFaceDesc base_faces[4] = {
502 {v3s16( 1, 0, 0), {v3s16(1, 0, 1), v3s16(1, 0, 0)}},
503 {v3s16(-1, 0, 0), {v3s16(0, 0, 0), v3s16(0, 0, 1)}},
504 {v3s16( 0, 0, 1), {v3s16(0, 0, 1), v3s16(1, 0, 1)}},
505 {v3s16( 0, 0, -1), {v3s16(1, 0, 0), v3s16(0, 0, 0)}},
507 static const UV base_vertices[4] = {
513 for (int i = 0; i < 4; i++) {
514 const LiquidFaceDesc &face = base_faces[i];
515 const NeighborData &neighbor = liquid_neighbors[face.dir.Z + 1][face.dir.X + 1];
517 // No face between nodes of the same liquid, unless there is node
518 // at the top to which it should be connected. Again, unless the face
519 // there would be inside the liquid
520 if (neighbor.is_same_liquid) {
523 if (!top_is_same_liquid)
525 if (neighbor.top_is_same_liquid)
529 if (!flowing && (neighbor.content == CONTENT_IGNORE))
532 const ContentFeatures &neighbor_features = nodedef->get(neighbor.content);
533 // Don't draw face if neighbor is blocking the view
534 if (neighbor_features.solidness == 2)
537 video::S3DVertex vertices[4];
538 for (int j = 0; j < 4; j++) {
539 const UV &vertex = base_vertices[j];
540 const v3s16 &base = face.p[vertex.u];
542 pos.X = (base.X - 0.5) * BS;
543 pos.Z = (base.Z - 0.5) * BS;
545 pos.Y = neighbor.is_same_liquid ? corner_levels[base.Z][base.X] : -0.5 * BS;
547 pos.Y = !top_is_same_liquid ? corner_levels[base.Z][base.X] : 0.5 * BS;
548 if (data->m_smooth_lighting)
549 color = blendLight(pos, tile_liquid.color);
551 vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, color, vertex.u, vertex.v);
553 collector->append(tile_liquid, vertices, 4, quad_indices, 6);
557 void MapblockMeshGenerator::drawLiquidTop(bool flowing)
559 // To get backface culling right, the vertices need to go
560 // clockwise around the front of the face. And we happened to
561 // calculate corner levels in exact reverse order.
562 static const int corner_resolve[4][2] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
564 video::S3DVertex vertices[4] = {
565 video::S3DVertex(-BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 0, 1),
566 video::S3DVertex( BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 1, 1),
567 video::S3DVertex( BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 1, 0),
568 video::S3DVertex(-BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 0, 0),
571 for (int i = 0; i < 4; i++) {
572 int u = corner_resolve[i][0];
573 int w = corner_resolve[i][1];
574 vertices[i].Pos.Y += corner_levels[w][u];
575 if (data->m_smooth_lighting)
576 vertices[i].Color = blendLight(vertices[i].Pos, tile_liquid_top.color);
577 vertices[i].Pos += origin;
581 // Default downwards-flowing texture animation goes from
582 // -Z towards +Z, thus the direction is +Z.
583 // Rotate texture to make animation go in flow direction
584 // Positive if liquid moves towards +Z
585 f32 dz = (corner_levels[0][0] + corner_levels[0][1]) -
586 (corner_levels[1][0] + corner_levels[1][1]);
587 // Positive if liquid moves towards +X
588 f32 dx = (corner_levels[0][0] + corner_levels[1][0]) -
589 (corner_levels[0][1] + corner_levels[1][1]);
590 f32 tcoord_angle = atan2(dz, dx) * core::RADTODEG;
591 v2f tcoord_center(0.5, 0.5);
592 v2f tcoord_translate(blockpos_nodes.Z + p.Z, blockpos_nodes.X + p.X);
593 tcoord_translate.rotateBy(tcoord_angle);
594 tcoord_translate.X -= floor(tcoord_translate.X);
595 tcoord_translate.Y -= floor(tcoord_translate.Y);
597 for (int i = 0; i < 4; i++) {
598 vertices[i].TCoords.rotateBy(tcoord_angle, tcoord_center);
599 vertices[i].TCoords += tcoord_translate;
602 std::swap(vertices[0].TCoords, vertices[2].TCoords);
605 collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
608 void MapblockMeshGenerator::drawLiquidNode(bool flowing)
610 prepareLiquidNodeDrawing(flowing);
611 getLiquidNeighborhood(flowing);
613 calculateCornerLevels();
616 drawLiquidSides(flowing);
617 if (!top_is_same_liquid)
618 drawLiquidTop(flowing);
621 void MapblockMeshGenerator::drawGlasslikeNode()
625 for (int face = 0; face < 6; face++) {
626 // Check this neighbor
627 v3s16 dir = g_6dirs[face];
628 v3s16 neighbor_pos = blockpos_nodes + p + dir;
629 MapNode neighbor = data->m_vmanip.getNodeNoExNoEmerge(neighbor_pos);
630 // Don't make face if neighbor is of same type
631 if (neighbor.getContent() == n.getContent())
634 v3f(-BS / 2, -BS / 2, BS / 2),
635 v3f( BS / 2, -BS / 2, BS / 2),
636 v3f( BS / 2, BS / 2, BS / 2),
637 v3f(-BS / 2, BS / 2, BS / 2),
639 for (int i = 0; i < 4; i++) {
640 // Rotations in the g_6dirs format
642 case 0: vertices[i].rotateXZBy( 0); break; // Z+
643 case 1: vertices[i].rotateYZBy(-90); break; // Y+
644 case 2: vertices[i].rotateXZBy(-90); break; // X+
645 case 3: vertices[i].rotateXZBy(180); break; // Z-
646 case 4: vertices[i].rotateYZBy( 90); break; // Y-
647 case 5: vertices[i].rotateXZBy( 90); break; // X-
650 drawQuad(vertices, dir);
654 void MapblockMeshGenerator::drawGlasslikeFramedNode()
657 for (int face = 0; face < 6; face++)
658 tiles[face] = getTile(g_6dirs[face]);
660 TileSpec glass_tiles[6];
661 if (tiles[0].texture && tiles[3].texture && tiles[4].texture) {
662 glass_tiles[0] = tiles[4];
663 glass_tiles[1] = tiles[0];
664 glass_tiles[2] = tiles[4];
665 glass_tiles[3] = tiles[4];
666 glass_tiles[4] = tiles[3];
667 glass_tiles[5] = tiles[4];
669 for (int face = 0; face < 6; face++)
670 glass_tiles[face] = tiles[4];
673 u8 param2 = n.getParam2();
674 bool H_merge = !(param2 & 128);
675 bool V_merge = !(param2 & 64);
678 static const float a = BS / 2;
679 static const float g = a - 0.003;
680 static const float b = .876 * ( BS / 2 );
682 static const aabb3f frame_edges[FRAMED_EDGE_COUNT] = {
683 aabb3f( b, b, -a, a, a, a), // y+
684 aabb3f(-a, b, -a, -b, a, a), // y+
685 aabb3f( b, -a, -a, a, -b, a), // y-
686 aabb3f(-a, -a, -a, -b, -b, a), // y-
687 aabb3f( b, -a, b, a, a, a), // x+
688 aabb3f( b, -a, -a, a, a, -b), // x+
689 aabb3f(-a, -a, b, -b, a, a), // x-
690 aabb3f(-a, -a, -a, -b, a, -b), // x-
691 aabb3f(-a, b, b, a, a, a), // z+
692 aabb3f(-a, -a, b, a, -b, a), // z+
693 aabb3f(-a, -a, -a, a, -b, -b), // z-
694 aabb3f(-a, b, -a, a, a, -b), // z-
696 static const aabb3f glass_faces[6] = {
697 aabb3f(-g, -g, g, g, g, g), // z+
698 aabb3f(-g, g, -g, g, g, g), // y+
699 aabb3f( g, -g, -g, g, g, g), // x+
700 aabb3f(-g, -g, -g, g, g, -g), // z-
701 aabb3f(-g, -g, -g, g, -g, g), // y-
702 aabb3f(-g, -g, -g, -g, g, g), // x-
705 // tables of neighbour (connect if same type and merge allowed),
706 // checked with g_26dirs
708 // 1 = connect, 0 = face visible
709 bool nb[FRAMED_NEIGHBOR_COUNT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
712 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};
713 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};
714 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};
715 const bool *check_nb = check_nb_all;
717 // neighbours checks for frames visibility
718 if (H_merge || V_merge) {
720 check_nb = check_nb_vertical; // vertical-only merge
722 check_nb = check_nb_horizontal; // horizontal-only merge
723 content_t current = n.getContent();
724 for (int i = 0; i < FRAMED_NEIGHBOR_COUNT; i++) {
727 v3s16 n2p = blockpos_nodes + p + g_26dirs[i];
728 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
729 content_t n2c = n2.getContent();
730 if (n2c == current || n2c == CONTENT_IGNORE)
737 static const u8 nb_triplet[FRAMED_EDGE_COUNT][3] = {
738 {1, 2, 7}, {1, 5, 6}, {4, 2, 15}, {4, 5, 14},
739 {2, 0, 11}, {2, 3, 13}, {5, 0, 10}, {5, 3, 12},
740 {0, 1, 8}, {0, 4, 16}, {3, 4, 17}, {3, 1, 9},
744 for (int edge = 0; edge < FRAMED_EDGE_COUNT; edge++) {
746 if (nb[nb_triplet[edge][2]])
747 edge_invisible = nb[nb_triplet[edge][0]] & nb[nb_triplet[edge][1]];
749 edge_invisible = nb[nb_triplet[edge][0]] ^ nb[nb_triplet[edge][1]];
752 drawAutoLightedCuboid(frame_edges[edge]);
755 for (int face = 0; face < 6; face++) {
758 tile = glass_tiles[face];
759 drawAutoLightedCuboid(glass_faces[face]);
762 if (param2 > 0 && f->special_tiles[0].texture) {
763 // Interior volume level is in range 0 .. 63,
764 // convert it to -0.5 .. 0.5
765 float vlev = (param2 / 63.0) * 2.0 - 1.0;
766 tile = getSpecialTile(*f, n, 0);
767 drawAutoLightedCuboid(aabb3f(-(nb[5] ? g : b),
771 (nb[1] ? g : b) * vlev,
776 void MapblockMeshGenerator::drawAllfacesNode()
778 static const aabb3f box(-BS / 2, -BS / 2, -BS / 2, BS / 2, BS / 2, BS / 2);
779 useDefaultTile(false);
780 drawAutoLightedCuboid(box);
783 void MapblockMeshGenerator::drawTorchlikeNode()
785 u8 wall = n.getWallMounted(nodedef);
788 case DWM_YP: tileindex = 1; break; // ceiling
789 case DWM_YN: tileindex = 0; break; // floor
790 default: tileindex = 2; // side (or invalid—should we care?)
792 useTile(tileindex, true);
794 float size = BS / 2 * f->visual_scale;
796 v3f(-size, -size, 0),
797 v3f( size, -size, 0),
801 for (int i = 0; i < 4; i++) {
803 case DWM_YP: vertices[i].rotateXZBy(-45); break;
804 case DWM_YN: vertices[i].rotateXZBy( 45); break;
805 case DWM_XP: vertices[i].rotateXZBy( 0); break;
806 case DWM_XN: vertices[i].rotateXZBy(180); break;
807 case DWM_ZP: vertices[i].rotateXZBy( 90); break;
808 case DWM_ZN: vertices[i].rotateXZBy(-90); break;
814 void MapblockMeshGenerator::drawSignlikeNode()
816 u8 wall = n.getWallMounted(nodedef);
818 static const float offset = BS / 16;
819 float size = BS / 2 * f->visual_scale;
820 // Wall at X+ of node
822 v3f(BS / 2 - offset, size, size),
823 v3f(BS / 2 - offset, size, -size),
824 v3f(BS / 2 - offset, -size, -size),
825 v3f(BS / 2 - offset, -size, size),
827 for (int i = 0; i < 4; i++) {
829 case DWM_YP: vertices[i].rotateXYBy( 90); break;
830 case DWM_YN: vertices[i].rotateXYBy(-90); break;
831 case DWM_XP: vertices[i].rotateXZBy( 0); break;
832 case DWM_XN: vertices[i].rotateXZBy(180); break;
833 case DWM_ZP: vertices[i].rotateXZBy( 90); break;
834 case DWM_ZN: vertices[i].rotateXZBy(-90); break;
840 void MapblockMeshGenerator::drawPlantlikeQuad(float rotation, float quad_offset,
841 bool offset_top_only)
844 v3f(-scale, -BS / 2, 0),
845 v3f( scale, -BS / 2, 0),
846 v3f( scale, -BS / 2 + scale * 2, 0),
847 v3f(-scale, -BS / 2 + scale * 2, 0),
849 if (random_offset_Y) {
850 PseudoRandom yrng(face_num++ | p.X << 16 | p.Z << 8 | p.Y << 24);
851 offset.Y = BS * ((yrng.next() % 16 / 16.0) * 0.125);
853 int offset_first_index = offset_top_only ? 2 : 0;
854 for (int i = 0; i < 4; i++) {
855 if (i >= offset_first_index)
856 vertices[i].Z += quad_offset;
857 vertices[i].rotateXZBy(rotation + rotate_degree);
858 vertices[i] += offset;
863 void MapblockMeshGenerator::drawPlantlikeNode()
866 draw_style = PLANT_STYLE_CROSS;
867 scale = BS / 2 * f->visual_scale;
868 offset = v3f(0, 0, 0);
870 random_offset_Y = false;
873 switch (f->param_type_2) {
874 case CPT2_MESHOPTIONS:
875 draw_style = PlantlikeStyle(n.param2 & MO_MASK_STYLE);
876 if (n.param2 & MO_BIT_SCALE_SQRT2)
878 if (n.param2 & MO_BIT_RANDOM_OFFSET) {
879 PseudoRandom rng(p.X << 8 | p.Z | p.Y << 16);
880 offset.X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
881 offset.Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
883 if (n.param2 & MO_BIT_RANDOM_OFFSET_Y)
884 random_offset_Y = true;
888 rotate_degree = n.param2 * 2;
895 switch (draw_style) {
896 case PLANT_STYLE_CROSS:
897 drawPlantlikeQuad(46);
898 drawPlantlikeQuad(-44);
901 case PLANT_STYLE_CROSS2:
902 drawPlantlikeQuad(91);
903 drawPlantlikeQuad(1);
906 case PLANT_STYLE_STAR:
907 drawPlantlikeQuad(121);
908 drawPlantlikeQuad(241);
909 drawPlantlikeQuad(1);
912 case PLANT_STYLE_HASH:
913 drawPlantlikeQuad( 1, BS / 4);
914 drawPlantlikeQuad( 91, BS / 4);
915 drawPlantlikeQuad(181, BS / 4);
916 drawPlantlikeQuad(271, BS / 4);
919 case PLANT_STYLE_HASH2:
920 drawPlantlikeQuad( 1, -BS / 2, true);
921 drawPlantlikeQuad( 91, -BS / 2, true);
922 drawPlantlikeQuad(181, -BS / 2, true);
923 drawPlantlikeQuad(271, -BS / 2, true);
928 void MapblockMeshGenerator::drawFirelikeQuad(float rotation, float opening_angle,
929 float offset_h, float offset_v)
932 v3f(-scale, -BS / 2, 0),
933 v3f( scale, -BS / 2, 0),
934 v3f( scale, -BS / 2 + scale * 2, 0),
935 v3f(-scale, -BS / 2 + scale * 2, 0),
937 for (int i = 0; i < 4; i++) {
938 vertices[i].rotateYZBy(opening_angle);
939 vertices[i].Z += offset_h;
940 vertices[i].rotateXZBy(rotation);
941 vertices[i].Y += offset_v;
946 void MapblockMeshGenerator::drawFirelikeNode()
949 scale = BS / 2 * f->visual_scale;
951 // Check for adjacent nodes
952 bool neighbors = false;
953 bool neighbor[6] = {0, 0, 0, 0, 0, 0};
954 content_t current = n.getContent();
955 for (int i = 0; i < 6; i++) {
956 v3s16 n2p = blockpos_nodes + p + g_6dirs[i];
957 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
958 content_t n2c = n2.getContent();
959 if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
964 bool drawBasicFire = neighbor[D6D_YN] || !neighbors;
965 bool drawBottomFire = neighbor[D6D_YP];
967 if (drawBasicFire || neighbor[D6D_ZP])
968 drawFirelikeQuad(0, -10, 0.4 * BS);
969 else if (drawBottomFire)
970 drawFirelikeQuad(0, 70, 0.47 * BS, 0.484 * BS);
972 if (drawBasicFire || neighbor[D6D_XN])
973 drawFirelikeQuad(90, -10, 0.4 * BS);
974 else if (drawBottomFire)
975 drawFirelikeQuad(90, 70, 0.47 * BS, 0.484 * BS);
977 if (drawBasicFire || neighbor[D6D_ZN])
978 drawFirelikeQuad(180, -10, 0.4 * BS);
979 else if (drawBottomFire)
980 drawFirelikeQuad(180, 70, 0.47 * BS, 0.484 * BS);
982 if (drawBasicFire || neighbor[D6D_XP])
983 drawFirelikeQuad(270, -10, 0.4 * BS);
984 else if (drawBottomFire)
985 drawFirelikeQuad(270, 70, 0.47 * BS, 0.484 * BS);
988 drawFirelikeQuad(45, 0, 0.0);
989 drawFirelikeQuad(-45, 0, 0.0);
993 void MapblockMeshGenerator::drawFencelikeNode()
995 useDefaultTile(false);
996 TileSpec tile_nocrack = tile;
997 tile_nocrack.material_flags &= ~MATERIAL_FLAG_CRACK;
999 // Put wood the right way around in the posts
1000 TileSpec tile_rot = tile;
1001 tile_rot.rotation = 1;
1003 static const f32 post_rad = BS / 8;
1004 static const f32 bar_rad = BS / 16;
1005 static const f32 bar_len = BS / 2 - post_rad;
1007 // The post - always present
1008 static const aabb3f post(-post_rad, -BS / 2, -post_rad,
1009 post_rad, BS / 2, post_rad);
1010 static const f32 postuv[24] = {
1011 0.375, 0.375, 0.625, 0.625,
1012 0.375, 0.375, 0.625, 0.625,
1013 0.000, 0.000, 0.250, 1.000,
1014 0.250, 0.000, 0.500, 1.000,
1015 0.500, 0.000, 0.750, 1.000,
1016 0.750, 0.000, 1.000, 1.000,
1019 drawAutoLightedCuboid(post, postuv);
1021 tile = tile_nocrack;
1023 // Now a section of fence, +X, if there's a post there
1026 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1027 const ContentFeatures *f2 = &nodedef->get(n2);
1028 if (f2->drawtype == NDT_FENCELIKE) {
1029 static const aabb3f bar_x1(BS / 2 - bar_len, BS / 4 - bar_rad, -bar_rad,
1030 BS / 2 + bar_len, BS / 4 + bar_rad, bar_rad);
1031 static const aabb3f bar_x2(BS / 2 - bar_len, -BS / 4 - bar_rad, -bar_rad,
1032 BS / 2 + bar_len, -BS / 4 + bar_rad, bar_rad);
1033 static const f32 xrailuv[24] = {
1034 0.000, 0.125, 1.000, 0.250,
1035 0.000, 0.250, 1.000, 0.375,
1036 0.375, 0.375, 0.500, 0.500,
1037 0.625, 0.625, 0.750, 0.750,
1038 0.000, 0.500, 1.000, 0.625,
1039 0.000, 0.875, 1.000, 1.000,
1041 drawAutoLightedCuboid(bar_x1, xrailuv);
1042 drawAutoLightedCuboid(bar_x2, xrailuv);
1045 // Now a section of fence, +Z, if there's a post there
1048 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1049 f2 = &nodedef->get(n2);
1050 if (f2->drawtype == NDT_FENCELIKE) {
1051 static const aabb3f bar_z1(-bar_rad, BS / 4 - bar_rad, BS / 2 - bar_len,
1052 bar_rad, BS / 4 + bar_rad, BS / 2 + bar_len);
1053 static const aabb3f bar_z2(-bar_rad, -BS / 4 - bar_rad, BS / 2 - bar_len,
1054 bar_rad, -BS / 4 + bar_rad, BS / 2 + bar_len);
1055 static const f32 zrailuv[24] = {
1056 0.1875, 0.0625, 0.3125, 0.3125, // cannot rotate; stretch
1057 0.2500, 0.0625, 0.3750, 0.3125, // for wood texture instead
1058 0.0000, 0.5625, 1.0000, 0.6875,
1059 0.0000, 0.3750, 1.0000, 0.5000,
1060 0.3750, 0.3750, 0.5000, 0.5000,
1061 0.6250, 0.6250, 0.7500, 0.7500,
1063 drawAutoLightedCuboid(bar_z1, zrailuv);
1064 drawAutoLightedCuboid(bar_z2, zrailuv);
1068 bool MapblockMeshGenerator::isSameRail(v3s16 dir)
1070 MapNode node2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
1071 if (node2.getContent() == n.getContent())
1073 const ContentFeatures &def2 = nodedef->get(node2);
1074 return ((def2.drawtype == NDT_RAILLIKE) &&
1075 (def2.getGroup(raillike_groupname) == raillike_group));
1078 void MapblockMeshGenerator::drawRaillikeNode()
1080 static const v3s16 direction[4] = {
1086 static const int slope_angle[4] = {0, 180, 90, -90};
1098 static const RailDesc rail_kinds[16] = {
1101 {straight, 0}, // . . . .
1102 {straight, 0}, // . . . +Z
1103 {straight, 0}, // . . -Z .
1104 {straight, 0}, // . . -Z +Z
1105 {straight, 90}, // . -X . .
1106 { curved, 180}, // . -X . +Z
1107 { curved, 270}, // . -X -Z .
1108 {junction, 180}, // . -X -Z +Z
1109 {straight, 90}, // +X . . .
1110 { curved, 90}, // +X . . +Z
1111 { curved, 0}, // +X . -Z .
1112 {junction, 0}, // +X . -Z +Z
1113 {straight, 90}, // +X -X . .
1114 {junction, 90}, // +X -X . +Z
1115 {junction, 270}, // +X -X -Z .
1116 { cross, 0}, // +X -X -Z +Z
1119 raillike_group = nodedef->get(n).getGroup(raillike_groupname);
1124 bool sloped = false;
1125 for (int dir = 0; dir < 4; dir++) {
1126 bool rail_above = isSameRail(direction[dir] + v3s16(0, 1, 0));
1129 angle = slope_angle[dir];
1132 isSameRail(direction[dir]) ||
1133 isSameRail(direction[dir] + v3s16(0, -1, 0)))
1138 tile_index = straight;
1140 tile_index = rail_kinds[code].tile_index;
1141 angle = rail_kinds[code].angle;
1144 useTile(tile_index, true);
1146 static const float offset = BS / 64;
1147 static const float size = BS / 2;
1148 float y2 = sloped ? size : -size;
1150 v3f(-size, -size + offset, -size),
1151 v3f( size, -size + offset, -size),
1152 v3f( size, y2 + offset, size),
1153 v3f(-size, y2 + offset, size),
1156 for (int i = 0; i < 4; i++)
1157 vertices[i].rotateXZBy(angle);
1161 void MapblockMeshGenerator::drawNodeboxNode()
1163 static const v3s16 tile_dirs[6] = {
1172 // we have this order for some reason...
1173 static const v3s16 connection_dirs[6] = {
1174 v3s16( 0, 1, 0), // top
1175 v3s16( 0, -1, 0), // bottom
1176 v3s16( 0, 0, -1), // front
1177 v3s16(-1, 0, 0), // left
1178 v3s16( 0, 0, 1), // back
1179 v3s16( 1, 0, 0), // right
1183 for (int face = 0; face < 6; face++) {
1184 // Handles facedir rotation for textures
1185 tiles[face] = getTile(tile_dirs[face]);
1188 // locate possible neighboring nodes to connect to
1189 int neighbors_set = 0;
1190 if (f->node_box.type == NODEBOX_CONNECTED) {
1191 for (int dir = 0; dir != 6; dir++) {
1192 int flag = 1 << dir;
1193 v3s16 p2 = blockpos_nodes + p + connection_dirs[dir];
1194 MapNode n2 = data->m_vmanip.getNodeNoEx(p2);
1195 if (nodedef->nodeboxConnects(n, n2, flag))
1196 neighbors_set |= flag;
1200 std::vector<aabb3f> boxes;
1201 n.getNodeBoxes(nodedef, &boxes, neighbors_set);
1202 for (std::vector<aabb3f>::iterator i = boxes.begin(); i != boxes.end(); ++i)
1203 drawAutoLightedCuboid(*i, NULL, tiles, 6);
1206 void MapblockMeshGenerator::drawMeshNode()
1210 bool private_mesh; // as a grab/drop pair is not thread-safe
1212 if (f->param_type_2 == CPT2_FACEDIR ||
1213 f->param_type_2 == CPT2_COLORED_FACEDIR) {
1214 facedir = n.getFaceDir(nodedef);
1215 } else if (f->param_type_2 == CPT2_WALLMOUNTED ||
1216 f->param_type_2 == CPT2_COLORED_WALLMOUNTED) {
1217 // Convert wallmounted to 6dfacedir.
1218 // When cache enabled, it is already converted.
1219 facedir = n.getWallMounted(nodedef);
1220 if (!enable_mesh_cache) {
1221 static const u8 wm_to_6d[6] = {20, 0, 16 + 1, 12 + 3, 8, 4 + 2};
1222 facedir = wm_to_6d[facedir];
1226 if (!data->m_smooth_lighting && f->mesh_ptr[facedir]) {
1227 // use cached meshes
1228 private_mesh = false;
1229 mesh = f->mesh_ptr[facedir];
1230 } else if (f->mesh_ptr[0]) {
1231 // no cache, clone and rotate mesh
1232 private_mesh = true;
1233 mesh = cloneMesh(f->mesh_ptr[0]);
1234 rotateMeshBy6dFacedir(mesh, facedir);
1235 recalculateBoundingBox(mesh);
1236 meshmanip->recalculateNormals(mesh, true, false);
1240 int mesh_buffer_count = mesh->getMeshBufferCount();
1241 for (int j = 0; j < mesh_buffer_count; j++) {
1243 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1244 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1245 int vertex_count = buf->getVertexCount();
1247 if (data->m_smooth_lighting) {
1248 // Mesh is always private here. So the lighting is applied to each
1249 // vertex right here.
1250 for (int k = 0; k < vertex_count; k++) {
1251 video::S3DVertex &vertex = vertices[k];
1252 vertex.Color = blendLight(vertex.Pos, vertex.Normal, tile.color);
1253 vertex.Pos += origin;
1255 collector->append(tile, vertices, vertex_count,
1256 buf->getIndices(), buf->getIndexCount());
1258 // Don't modify the mesh, it may not be private here.
1259 // Instead, let the collector process colors, etc.
1260 collector->append(tile, vertices, vertex_count,
1261 buf->getIndices(), buf->getIndexCount(), origin,
1262 color, f->light_source);
1269 // also called when the drawtype is known but should have been pre-converted
1270 void MapblockMeshGenerator::errorUnknownDrawtype()
1272 infostream << "Got drawtype " << f->drawtype << std::endl;
1273 FATAL_ERROR("Unknown drawtype");
1276 void MapblockMeshGenerator::drawNode()
1278 if (data->m_smooth_lighting)
1279 getSmoothLightFrame();
1281 light = getInteriorLight(n, 1, nodedef);
1282 switch (f->drawtype) {
1283 case NDT_LIQUID: drawLiquidNode(false); break;
1284 case NDT_FLOWINGLIQUID: drawLiquidNode(true); break;
1285 case NDT_GLASSLIKE: drawGlasslikeNode(); break;
1286 case NDT_GLASSLIKE_FRAMED: drawGlasslikeFramedNode(); break;
1287 case NDT_ALLFACES: drawAllfacesNode(); break;
1288 case NDT_TORCHLIKE: drawTorchlikeNode(); break;
1289 case NDT_SIGNLIKE: drawSignlikeNode(); break;
1290 case NDT_PLANTLIKE: drawPlantlikeNode(); break;
1291 case NDT_FIRELIKE: drawFirelikeNode(); break;
1292 case NDT_FENCELIKE: drawFencelikeNode(); break;
1293 case NDT_RAILLIKE: drawRaillikeNode(); break;
1294 case NDT_NODEBOX: drawNodeboxNode(); break;
1295 case NDT_MESH: drawMeshNode(); break;
1296 default: errorUnknownDrawtype(); break;
1301 TODO: Fix alpha blending for special nodes
1302 Currently only the last element rendered is blended correct
1304 void MapblockMeshGenerator::generate()
1306 for (p.Z = 0; p.Z < MAP_BLOCKSIZE; p.Z++)
1307 for (p.Y = 0; p.Y < MAP_BLOCKSIZE; p.Y++)
1308 for (p.X = 0; p.X < MAP_BLOCKSIZE; p.X++) {
1309 n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1310 f = &nodedef->get(n);
1311 // Solid nodes are drawn by MapBlockMesh
1312 if (f->solidness != 0)
1314 if (f->drawtype == NDT_AIRLIKE)
1316 origin = intToFloat(p, BS);