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>
29 #include "client/renderingengine.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 const 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 meshmanip = RenderingEngine::get_scene_manager()->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, u8 set_flags, u8 reset_flags, bool special)
79 getSpecialTile(index, &tile, p == data->m_crack_pos_relative);
81 getNodeTileN(n, p, index, data, tile);
82 if (!data->m_smooth_lighting)
83 color = encode_light(light, f->light_source);
84 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) {
85 tile.layers[layer].material_flags |= set_flags;
86 tile.layers[layer].material_flags &= ~reset_flags;
90 void MapblockMeshGenerator::getTile(v3s16 direction, TileSpec *tile)
92 getNodeTile(n, p, direction, data, *tile);
96 * Returns the i-th special tile for a map node.
98 void MapblockMeshGenerator::getSpecialTile(int index, TileSpec *tile, bool apply_crack)
100 *tile = f->special_tiles[index];
101 TileLayer *top_layer = NULL;
102 for (int layernum = 0; layernum < MAX_TILE_LAYERS; layernum++) {
103 TileLayer *layer = &tile->layers[layernum];
104 if (layer->texture_id == 0)
107 if (!layer->has_color)
108 n.getColor(*f, &layer->color);
111 top_layer->material_flags |= MATERIAL_FLAG_CRACK;
114 void MapblockMeshGenerator::drawQuad(v3f *coords, const v3s16 &normal,
115 float vertical_tiling)
117 const v2f tcoords[4] = {v2f(0.0, 0.0), v2f(1.0, 0.0),
118 v2f(1.0, vertical_tiling), v2f(0.0, vertical_tiling)};
119 video::S3DVertex vertices[4];
120 bool shade_face = !f->light_source && (normal != v3s16(0, 0, 0));
121 v3f normal2(normal.X, normal.Y, normal.Z);
122 for (int j = 0; j < 4; j++) {
123 vertices[j].Pos = coords[j] + origin;
124 vertices[j].Normal = normal2;
125 if (data->m_smooth_lighting)
126 vertices[j].Color = blendLightColor(coords[j]);
128 vertices[j].Color = color;
130 applyFacesShading(vertices[j].Color, normal2);
131 vertices[j].TCoords = tcoords[j];
133 collector->append(tile, vertices, 4, quad_indices, 6);
137 // tiles - the tiles (materials) to use (for all 6 faces)
138 // tilecount - number of entries in tiles, 1<=tilecount<=6
139 // lights - vertex light levels. The order is the same as in light_dirs.
140 // NULL may be passed if smooth lighting is disabled.
141 // txc - texture coordinates - this is a list of texture coordinates
142 // for the opposite corners of each face - therefore, there
143 // should be (2+2)*6=24 values in the list. The order of
144 // the faces in the list is up-down-right-left-back-front
145 // (compatible with ContentFeatures).
146 void MapblockMeshGenerator::drawCuboid(const aabb3f &box,
147 TileSpec *tiles, int tilecount, const u16 *lights, const f32 *txc)
149 assert(tilecount >= 1 && tilecount <= 6); // pre-condition
151 v3f min = box.MinEdge;
152 v3f max = box.MaxEdge;
154 video::SColor colors[6];
155 if (!data->m_smooth_lighting) {
156 for (int face = 0; face != 6; ++face) {
157 colors[face] = encode_light(light, f->light_source);
159 if (!f->light_source) {
160 applyFacesShading(colors[0], v3f(0, 1, 0));
161 applyFacesShading(colors[1], v3f(0, -1, 0));
162 applyFacesShading(colors[2], v3f(1, 0, 0));
163 applyFacesShading(colors[3], v3f(-1, 0, 0));
164 applyFacesShading(colors[4], v3f(0, 0, 1));
165 applyFacesShading(colors[5], v3f(0, 0, -1));
169 video::S3DVertex vertices[24] = {
171 video::S3DVertex(min.X, max.Y, max.Z, 0, 1, 0, colors[0], txc[0], txc[1]),
172 video::S3DVertex(max.X, max.Y, max.Z, 0, 1, 0, colors[0], txc[2], txc[1]),
173 video::S3DVertex(max.X, max.Y, min.Z, 0, 1, 0, colors[0], txc[2], txc[3]),
174 video::S3DVertex(min.X, max.Y, min.Z, 0, 1, 0, colors[0], txc[0], txc[3]),
176 video::S3DVertex(min.X, min.Y, min.Z, 0, -1, 0, colors[1], txc[4], txc[5]),
177 video::S3DVertex(max.X, min.Y, min.Z, 0, -1, 0, colors[1], txc[6], txc[5]),
178 video::S3DVertex(max.X, min.Y, max.Z, 0, -1, 0, colors[1], txc[6], txc[7]),
179 video::S3DVertex(min.X, min.Y, max.Z, 0, -1, 0, colors[1], txc[4], txc[7]),
181 video::S3DVertex(max.X, max.Y, min.Z, 1, 0, 0, colors[2], txc[ 8], txc[9]),
182 video::S3DVertex(max.X, max.Y, max.Z, 1, 0, 0, colors[2], txc[10], txc[9]),
183 video::S3DVertex(max.X, min.Y, max.Z, 1, 0, 0, colors[2], txc[10], txc[11]),
184 video::S3DVertex(max.X, min.Y, min.Z, 1, 0, 0, colors[2], txc[ 8], txc[11]),
186 video::S3DVertex(min.X, max.Y, max.Z, -1, 0, 0, colors[3], txc[12], txc[13]),
187 video::S3DVertex(min.X, max.Y, min.Z, -1, 0, 0, colors[3], txc[14], txc[13]),
188 video::S3DVertex(min.X, min.Y, min.Z, -1, 0, 0, colors[3], txc[14], txc[15]),
189 video::S3DVertex(min.X, min.Y, max.Z, -1, 0, 0, colors[3], txc[12], txc[15]),
191 video::S3DVertex(max.X, max.Y, max.Z, 0, 0, 1, colors[4], txc[16], txc[17]),
192 video::S3DVertex(min.X, max.Y, max.Z, 0, 0, 1, colors[4], txc[18], txc[17]),
193 video::S3DVertex(min.X, min.Y, max.Z, 0, 0, 1, colors[4], txc[18], txc[19]),
194 video::S3DVertex(max.X, min.Y, max.Z, 0, 0, 1, colors[4], txc[16], txc[19]),
196 video::S3DVertex(min.X, max.Y, min.Z, 0, 0, -1, colors[5], txc[20], txc[21]),
197 video::S3DVertex(max.X, max.Y, min.Z, 0, 0, -1, colors[5], txc[22], txc[21]),
198 video::S3DVertex(max.X, min.Y, min.Z, 0, 0, -1, colors[5], txc[22], txc[23]),
199 video::S3DVertex(min.X, min.Y, min.Z, 0, 0, -1, colors[5], txc[20], txc[23]),
202 static const u8 light_indices[24] = {
211 for (int face = 0; face < 6; face++) {
212 int tileindex = MYMIN(face, tilecount - 1);
213 const TileSpec &tile = tiles[tileindex];
214 for (int j = 0; j < 4; j++) {
215 video::S3DVertex &vertex = vertices[face * 4 + j];
216 v2f &tcoords = vertex.TCoords;
217 switch (tile.rotation) {
221 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
224 tcoords.rotateBy(180, irr::core::vector2df(0, 0));
227 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
230 tcoords.X = 1.0 - tcoords.X;
231 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
234 tcoords.X = 1.0 - tcoords.X;
235 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
238 tcoords.Y = 1.0 - tcoords.Y;
239 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
242 tcoords.Y = 1.0 - tcoords.Y;
243 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
246 tcoords.X = 1.0 - tcoords.X;
249 tcoords.Y = 1.0 - tcoords.Y;
257 if (data->m_smooth_lighting) {
258 for (int j = 0; j < 24; ++j) {
259 vertices[j].Color = encode_light(lights[light_indices[j]],
261 if (!f->light_source)
262 applyFacesShading(vertices[j].Color, vertices[j].Normal);
266 // Add to mesh collector
267 for (int k = 0; k < 6; ++k) {
268 int tileindex = MYMIN(k, tilecount - 1);
269 collector->append(tiles[tileindex], vertices + 4 * k, 4, quad_indices, 6);
273 // Gets the base lighting values for a node
274 void MapblockMeshGenerator::getSmoothLightFrame()
276 for (int k = 0; k < 8; ++k) {
277 u16 light = getSmoothLight(blockpos_nodes + p, light_dirs[k], data);
278 frame.lightsA[k] = light & 0xff;
279 frame.lightsB[k] = light >> 8;
283 // Calculates vertex light level
284 // vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
285 u16 MapblockMeshGenerator::blendLight(const v3f &vertex_pos)
287 f32 x = core::clamp(vertex_pos.X / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
288 f32 y = core::clamp(vertex_pos.Y / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
289 f32 z = core::clamp(vertex_pos.Z / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
292 for (int k = 0; k < 8; ++k) {
293 f32 dx = (k & 4) ? x : 1 - x;
294 f32 dy = (k & 2) ? y : 1 - y;
295 f32 dz = (k & 1) ? z : 1 - z;
296 lightA += dx * dy * dz * frame.lightsA[k];
297 lightB += dx * dy * dz * frame.lightsB[k];
300 core::clamp(core::round32(lightA), 0, 255) |
301 core::clamp(core::round32(lightB), 0, 255) << 8;
304 // Calculates vertex color to be used in mapblock mesh
305 // vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
306 // tile_color - node's tile color
307 video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos)
309 u16 light = blendLight(vertex_pos);
310 return encode_light(light, f->light_source);
313 video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos,
314 const v3f &vertex_normal)
316 video::SColor color = blendLightColor(vertex_pos);
317 if (!f->light_source)
318 applyFacesShading(color, vertex_normal);
322 void MapblockMeshGenerator::generateCuboidTextureCoords(const aabb3f &box, f32 *coords)
324 f32 tx1 = (box.MinEdge.X / BS) + 0.5;
325 f32 ty1 = (box.MinEdge.Y / BS) + 0.5;
326 f32 tz1 = (box.MinEdge.Z / BS) + 0.5;
327 f32 tx2 = (box.MaxEdge.X / BS) + 0.5;
328 f32 ty2 = (box.MaxEdge.Y / BS) + 0.5;
329 f32 tz2 = (box.MaxEdge.Z / BS) + 0.5;
331 tx1, 1 - tz2, tx2, 1 - tz1, // up
332 tx1, tz1, tx2, tz2, // down
333 tz1, 1 - ty2, tz2, 1 - ty1, // right
334 1 - tz2, 1 - ty2, 1 - tz1, 1 - ty1, // left
335 1 - tx2, 1 - ty2, 1 - tx1, 1 - ty1, // back
336 tx1, 1 - ty2, tx2, 1 - ty1, // front
338 for (int i = 0; i != 24; ++i)
342 void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc,
343 TileSpec *tiles, int tile_count)
345 f32 texture_coord_buf[24];
346 f32 dx1 = box.MinEdge.X;
347 f32 dy1 = box.MinEdge.Y;
348 f32 dz1 = box.MinEdge.Z;
349 f32 dx2 = box.MaxEdge.X;
350 f32 dy2 = box.MaxEdge.Y;
351 f32 dz2 = box.MaxEdge.Z;
352 box.MinEdge += origin;
353 box.MaxEdge += origin;
355 generateCuboidTextureCoords(box, texture_coord_buf);
356 txc = texture_coord_buf;
362 if (data->m_smooth_lighting) {
364 for (int j = 0; j < 8; ++j) {
366 d.X = (j & 4) ? dx2 : dx1;
367 d.Y = (j & 2) ? dy2 : dy1;
368 d.Z = (j & 1) ? dz2 : dz1;
369 lights[j] = blendLight(d);
371 drawCuboid(box, tiles, tile_count, lights, txc);
373 drawCuboid(box, tiles, tile_count, NULL, txc);
377 void MapblockMeshGenerator::prepareLiquidNodeDrawing()
379 getSpecialTile(0, &tile_liquid_top);
380 getSpecialTile(1, &tile_liquid);
382 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y + 1, p.Z));
383 c_flowing = nodedef->getId(f->liquid_alternative_flowing);
384 c_source = nodedef->getId(f->liquid_alternative_source);
385 top_is_same_liquid = (ntop.getContent() == c_flowing) || (ntop.getContent() == c_source);
387 if (data->m_smooth_lighting)
388 return; // don't need to pre-compute anything in this case
390 if (f->light_source != 0) {
391 // If this liquid emits light and doesn't contain light, draw
392 // it at what it emits, for an increased effect
393 light = decode_light(f->light_source);
394 light = light | (light << 8);
395 } else if (nodedef->get(ntop).param_type == CPT_LIGHT) {
396 // Otherwise, use the light of the node on top if possible
397 light = getInteriorLight(ntop, 0, nodedef);
400 color_liquid_top = encode_light(light, f->light_source);
401 color = encode_light(light, f->light_source);
404 void MapblockMeshGenerator::getLiquidNeighborhood()
406 u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8);
408 for (int w = -1; w <= 1; w++)
409 for (int u = -1; u <= 1; u++) {
410 NeighborData &neighbor = liquid_neighbors[w + 1][u + 1];
411 v3s16 p2 = p + v3s16(u, 0, w);
412 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
413 neighbor.content = n2.getContent();
414 neighbor.level = -0.5 * BS;
415 neighbor.is_same_liquid = false;
416 neighbor.top_is_same_liquid = false;
418 if (neighbor.content == CONTENT_IGNORE)
421 if (neighbor.content == c_source) {
422 neighbor.is_same_liquid = true;
423 neighbor.level = 0.5 * BS;
424 } else if (neighbor.content == c_flowing) {
425 neighbor.is_same_liquid = true;
426 u8 liquid_level = (n2.param2 & LIQUID_LEVEL_MASK);
427 if (liquid_level <= LIQUID_LEVEL_MAX + 1 - range)
430 liquid_level -= (LIQUID_LEVEL_MAX + 1 - range);
431 neighbor.level = (-0.5 + (liquid_level + 0.5) / range) * BS;
434 // Check node above neighbor.
435 // NOTE: This doesn't get executed if neighbor
438 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
439 if (n2.getContent() == c_source || n2.getContent() == c_flowing)
440 neighbor.top_is_same_liquid = true;
444 void MapblockMeshGenerator::calculateCornerLevels()
446 for (int k = 0; k < 2; k++)
447 for (int i = 0; i < 2; i++)
448 corner_levels[k][i] = getCornerLevel(i, k);
451 f32 MapblockMeshGenerator::getCornerLevel(int i, int k)
456 for (int dk = 0; dk < 2; dk++)
457 for (int di = 0; di < 2; di++) {
458 NeighborData &neighbor_data = liquid_neighbors[k + dk][i + di];
459 content_t content = neighbor_data.content;
461 // If top is liquid, draw starting from top of node
462 if (neighbor_data.top_is_same_liquid)
465 // Source always has the full height
466 if (content == c_source)
469 // Flowing liquid has level information
470 if (content == c_flowing) {
471 sum += neighbor_data.level;
473 } else if (content == CONTENT_AIR) {
476 return -0.5 * BS + 0.2;
484 void MapblockMeshGenerator::drawLiquidSides()
486 struct LiquidFaceDesc {
488 v3s16 p[2]; // XZ only; 1 means +, 0 means -
493 static const LiquidFaceDesc base_faces[4] = {
494 {v3s16( 1, 0, 0), {v3s16(1, 0, 1), v3s16(1, 0, 0)}},
495 {v3s16(-1, 0, 0), {v3s16(0, 0, 0), v3s16(0, 0, 1)}},
496 {v3s16( 0, 0, 1), {v3s16(0, 0, 1), v3s16(1, 0, 1)}},
497 {v3s16( 0, 0, -1), {v3s16(1, 0, 0), v3s16(0, 0, 0)}},
499 static const UV base_vertices[4] = {
505 for (int i = 0; i < 4; i++) {
506 const LiquidFaceDesc &face = base_faces[i];
507 const NeighborData &neighbor = liquid_neighbors[face.dir.Z + 1][face.dir.X + 1];
509 // No face between nodes of the same liquid, unless there is node
510 // at the top to which it should be connected. Again, unless the face
511 // there would be inside the liquid
512 if (neighbor.is_same_liquid) {
513 if (!top_is_same_liquid)
515 if (neighbor.top_is_same_liquid)
519 const ContentFeatures &neighbor_features = nodedef->get(neighbor.content);
520 // Don't draw face if neighbor is blocking the view
521 if (neighbor_features.solidness == 2)
524 video::S3DVertex vertices[4];
525 for (int j = 0; j < 4; j++) {
526 const UV &vertex = base_vertices[j];
527 const v3s16 &base = face.p[vertex.u];
529 pos.X = (base.X - 0.5) * BS;
530 pos.Z = (base.Z - 0.5) * BS;
532 pos.Y = neighbor.is_same_liquid ? corner_levels[base.Z][base.X] : -0.5 * BS;
534 pos.Y = !top_is_same_liquid ? corner_levels[base.Z][base.X] : 0.5 * BS;
535 if (data->m_smooth_lighting)
536 color = blendLightColor(pos);
538 vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, color, vertex.u, vertex.v);
540 collector->append(tile_liquid, vertices, 4, quad_indices, 6);
544 void MapblockMeshGenerator::drawLiquidTop()
546 // To get backface culling right, the vertices need to go
547 // clockwise around the front of the face. And we happened to
548 // calculate corner levels in exact reverse order.
549 static const int corner_resolve[4][2] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
551 video::S3DVertex vertices[4] = {
552 video::S3DVertex(-BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 0, 1),
553 video::S3DVertex( BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 1, 1),
554 video::S3DVertex( BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 1, 0),
555 video::S3DVertex(-BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 0, 0),
558 for (int i = 0; i < 4; i++) {
559 int u = corner_resolve[i][0];
560 int w = corner_resolve[i][1];
561 vertices[i].Pos.Y += corner_levels[w][u];
562 if (data->m_smooth_lighting)
563 vertices[i].Color = blendLightColor(vertices[i].Pos);
564 vertices[i].Pos += origin;
567 // Default downwards-flowing texture animation goes from
568 // -Z towards +Z, thus the direction is +Z.
569 // Rotate texture to make animation go in flow direction
570 // Positive if liquid moves towards +Z
571 f32 dz = (corner_levels[0][0] + corner_levels[0][1]) -
572 (corner_levels[1][0] + corner_levels[1][1]);
573 // Positive if liquid moves towards +X
574 f32 dx = (corner_levels[0][0] + corner_levels[1][0]) -
575 (corner_levels[0][1] + corner_levels[1][1]);
576 f32 tcoord_angle = atan2(dz, dx) * core::RADTODEG;
577 v2f tcoord_center(0.5, 0.5);
578 v2f tcoord_translate(blockpos_nodes.Z + p.Z, blockpos_nodes.X + p.X);
579 tcoord_translate.rotateBy(tcoord_angle);
580 tcoord_translate.X -= floor(tcoord_translate.X);
581 tcoord_translate.Y -= floor(tcoord_translate.Y);
583 for (int i = 0; i < 4; i++) {
584 vertices[i].TCoords.rotateBy(tcoord_angle, tcoord_center);
585 vertices[i].TCoords += tcoord_translate;
588 std::swap(vertices[0].TCoords, vertices[2].TCoords);
590 collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
593 void MapblockMeshGenerator::drawLiquidNode()
595 prepareLiquidNodeDrawing();
596 getLiquidNeighborhood();
597 calculateCornerLevels();
599 if (!top_is_same_liquid)
603 void MapblockMeshGenerator::drawGlasslikeNode()
607 for (int face = 0; face < 6; face++) {
608 // Check this neighbor
609 v3s16 dir = g_6dirs[face];
610 v3s16 neighbor_pos = blockpos_nodes + p + dir;
611 MapNode neighbor = data->m_vmanip.getNodeNoExNoEmerge(neighbor_pos);
612 // Don't make face if neighbor is of same type
613 if (neighbor.getContent() == n.getContent())
617 v3f(-BS / 2, BS / 2, -BS / 2),
618 v3f( BS / 2, BS / 2, -BS / 2),
619 v3f( BS / 2, -BS / 2, -BS / 2),
620 v3f(-BS / 2, -BS / 2, -BS / 2),
622 for (int i = 0; i < 4; i++) {
624 case D6D_ZP: vertices[i].rotateXZBy(180); break;
625 case D6D_YP: vertices[i].rotateYZBy( 90); break;
626 case D6D_XP: vertices[i].rotateXZBy( 90); break;
627 case D6D_ZN: vertices[i].rotateXZBy( 0); break;
628 case D6D_YN: vertices[i].rotateYZBy(-90); break;
629 case D6D_XN: vertices[i].rotateXZBy(-90); break;
632 drawQuad(vertices, dir);
636 void MapblockMeshGenerator::drawGlasslikeFramedNode()
639 for (int face = 0; face < 6; face++)
640 getTile(g_6dirs[face], &tiles[face]);
642 TileSpec glass_tiles[6];
643 if (tiles[1].layers[0].texture &&
644 tiles[2].layers[0].texture &&
645 tiles[3].layers[0].texture) {
646 glass_tiles[0] = tiles[4];
647 glass_tiles[1] = tiles[0];
648 glass_tiles[2] = tiles[4];
649 glass_tiles[3] = tiles[4];
650 glass_tiles[4] = tiles[3];
651 glass_tiles[5] = tiles[4];
653 for (int face = 0; face < 6; face++)
654 glass_tiles[face] = tiles[4];
657 u8 param2 = n.getParam2();
658 bool H_merge = !(param2 & 128);
659 bool V_merge = !(param2 & 64);
662 static const float a = BS / 2;
663 static const float g = a - 0.003;
664 static const float b = .876 * ( BS / 2 );
666 static const aabb3f frame_edges[FRAMED_EDGE_COUNT] = {
667 aabb3f( b, b, -a, a, a, a), // y+
668 aabb3f(-a, b, -a, -b, a, a), // y+
669 aabb3f( b, -a, -a, a, -b, a), // y-
670 aabb3f(-a, -a, -a, -b, -b, a), // y-
671 aabb3f( b, -a, b, a, a, a), // x+
672 aabb3f( b, -a, -a, a, a, -b), // x+
673 aabb3f(-a, -a, b, -b, a, a), // x-
674 aabb3f(-a, -a, -a, -b, a, -b), // x-
675 aabb3f(-a, b, b, a, a, a), // z+
676 aabb3f(-a, -a, b, a, -b, a), // z+
677 aabb3f(-a, -a, -a, a, -b, -b), // z-
678 aabb3f(-a, b, -a, a, a, -b), // z-
680 static const aabb3f glass_faces[6] = {
681 aabb3f(-g, -g, g, g, g, g), // z+
682 aabb3f(-g, g, -g, g, g, g), // y+
683 aabb3f( g, -g, -g, g, g, g), // x+
684 aabb3f(-g, -g, -g, g, g, -g), // z-
685 aabb3f(-g, -g, -g, g, -g, g), // y-
686 aabb3f(-g, -g, -g, -g, g, g), // x-
689 // tables of neighbour (connect if same type and merge allowed),
690 // checked with g_26dirs
692 // 1 = connect, 0 = face visible
693 bool nb[FRAMED_NEIGHBOR_COUNT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
696 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};
697 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};
698 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};
699 const bool *check_nb = check_nb_all;
701 // neighbours checks for frames visibility
702 if (H_merge || V_merge) {
704 check_nb = check_nb_vertical; // vertical-only merge
706 check_nb = check_nb_horizontal; // horizontal-only merge
707 content_t current = n.getContent();
708 for (int i = 0; i < FRAMED_NEIGHBOR_COUNT; i++) {
711 v3s16 n2p = blockpos_nodes + p + g_26dirs[i];
712 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
713 content_t n2c = n2.getContent();
714 if (n2c == current || n2c == CONTENT_IGNORE)
721 static const u8 nb_triplet[FRAMED_EDGE_COUNT][3] = {
722 {1, 2, 7}, {1, 5, 6}, {4, 2, 15}, {4, 5, 14},
723 {2, 0, 11}, {2, 3, 13}, {5, 0, 10}, {5, 3, 12},
724 {0, 1, 8}, {0, 4, 16}, {3, 4, 17}, {3, 1, 9},
728 for (int edge = 0; edge < FRAMED_EDGE_COUNT; edge++) {
730 if (nb[nb_triplet[edge][2]])
731 edge_invisible = nb[nb_triplet[edge][0]] & nb[nb_triplet[edge][1]];
733 edge_invisible = nb[nb_triplet[edge][0]] ^ nb[nb_triplet[edge][1]];
736 drawAutoLightedCuboid(frame_edges[edge]);
739 for (int face = 0; face < 6; face++) {
742 tile = glass_tiles[face];
743 drawAutoLightedCuboid(glass_faces[face]);
746 // Optionally render internal liquid level defined by param2
747 // Liquid is textured with 1 tile defined in nodedef 'special_tiles'
748 if (param2 > 0 && f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL &&
749 f->special_tiles[0].layers[0].texture) {
750 // Internal liquid level has param2 range 0 .. 63,
751 // convert it to -0.5 .. 0.5
752 float vlev = (param2 / 63.0) * 2.0 - 1.0;
753 getSpecialTile(0, &tile);
754 drawAutoLightedCuboid(aabb3f(-(nb[5] ? g : b),
758 (nb[1] ? g : b) * vlev,
763 void MapblockMeshGenerator::drawAllfacesNode()
765 static const aabb3f box(-BS / 2, -BS / 2, -BS / 2, BS / 2, BS / 2, BS / 2);
767 drawAutoLightedCuboid(box);
770 void MapblockMeshGenerator::drawTorchlikeNode()
772 u8 wall = n.getWallMounted(nodedef);
775 case DWM_YP: tileindex = 1; break; // ceiling
776 case DWM_YN: tileindex = 0; break; // floor
777 default: tileindex = 2; // side (or invalid—should we care?)
779 useTile(tileindex, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
781 float size = BS / 2 * f->visual_scale;
785 v3f( size, -size, 0),
786 v3f(-size, -size, 0),
788 for (int i = 0; i < 4; i++) {
790 case DWM_YP: vertices[i].rotateXZBy(-45); break;
791 case DWM_YN: vertices[i].rotateXZBy( 45); break;
792 case DWM_XP: vertices[i].rotateXZBy( 0); break;
793 case DWM_XN: vertices[i].rotateXZBy(180); break;
794 case DWM_ZP: vertices[i].rotateXZBy( 90); break;
795 case DWM_ZN: vertices[i].rotateXZBy(-90); break;
801 void MapblockMeshGenerator::drawSignlikeNode()
803 u8 wall = n.getWallMounted(nodedef);
804 useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
805 static const float offset = BS / 16;
806 float size = BS / 2 * f->visual_scale;
807 // Wall at X+ of node
809 v3f(BS / 2 - offset, size, size),
810 v3f(BS / 2 - offset, size, -size),
811 v3f(BS / 2 - offset, -size, -size),
812 v3f(BS / 2 - offset, -size, size),
814 for (int i = 0; i < 4; i++) {
816 case DWM_YP: vertices[i].rotateXYBy( 90); break;
817 case DWM_YN: vertices[i].rotateXYBy(-90); break;
818 case DWM_XP: vertices[i].rotateXZBy( 0); break;
819 case DWM_XN: vertices[i].rotateXZBy(180); break;
820 case DWM_ZP: vertices[i].rotateXZBy( 90); break;
821 case DWM_ZN: vertices[i].rotateXZBy(-90); break;
827 void MapblockMeshGenerator::drawPlantlikeQuad(float rotation, float quad_offset,
828 bool offset_top_only)
831 v3f(-scale, -BS / 2 + 2.0 * scale * plant_height, 0),
832 v3f( scale, -BS / 2 + 2.0 * scale * plant_height, 0),
833 v3f( scale, -BS / 2, 0),
834 v3f(-scale, -BS / 2, 0),
836 if (random_offset_Y) {
837 PseudoRandom yrng(face_num++ | p.X << 16 | p.Z << 8 | p.Y << 24);
838 offset.Y = -BS * ((yrng.next() % 16 / 16.0) * 0.125);
840 int offset_count = offset_top_only ? 2 : 4;
841 for (int i = 0; i < offset_count; i++)
842 vertices[i].Z += quad_offset;
843 for (int i = 0; i < 4; i++) {
844 vertices[i].rotateXZBy(rotation + rotate_degree);
845 vertices[i] += offset;
847 drawQuad(vertices, v3s16(0, 0, 0), plant_height);
850 void MapblockMeshGenerator::drawPlantlike()
852 draw_style = PLANT_STYLE_CROSS;
853 scale = BS / 2 * f->visual_scale;
854 offset = v3f(0, 0, 0);
856 random_offset_Y = false;
860 switch (f->param_type_2) {
861 case CPT2_MESHOPTIONS:
862 draw_style = PlantlikeStyle(n.param2 & MO_MASK_STYLE);
863 if (n.param2 & MO_BIT_SCALE_SQRT2)
865 if (n.param2 & MO_BIT_RANDOM_OFFSET) {
866 PseudoRandom rng(p.X << 8 | p.Z | p.Y << 16);
867 offset.X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
868 offset.Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
870 if (n.param2 & MO_BIT_RANDOM_OFFSET_Y)
871 random_offset_Y = true;
875 rotate_degree = n.param2 * 2;
879 plant_height = n.param2 / 16.0;
886 switch (draw_style) {
887 case PLANT_STYLE_CROSS:
888 drawPlantlikeQuad(46);
889 drawPlantlikeQuad(-44);
892 case PLANT_STYLE_CROSS2:
893 drawPlantlikeQuad(91);
894 drawPlantlikeQuad(1);
897 case PLANT_STYLE_STAR:
898 drawPlantlikeQuad(121);
899 drawPlantlikeQuad(241);
900 drawPlantlikeQuad(1);
903 case PLANT_STYLE_HASH:
904 drawPlantlikeQuad( 1, BS / 4);
905 drawPlantlikeQuad( 91, BS / 4);
906 drawPlantlikeQuad(181, BS / 4);
907 drawPlantlikeQuad(271, BS / 4);
910 case PLANT_STYLE_HASH2:
911 drawPlantlikeQuad( 1, -BS / 2, true);
912 drawPlantlikeQuad( 91, -BS / 2, true);
913 drawPlantlikeQuad(181, -BS / 2, true);
914 drawPlantlikeQuad(271, -BS / 2, true);
919 void MapblockMeshGenerator::drawPlantlikeNode()
925 void MapblockMeshGenerator::drawPlantlikeRootedNode()
927 useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, 0, true);
928 origin += v3f(0.0, BS, 0.0);
930 if (data->m_smooth_lighting) {
931 getSmoothLightFrame();
933 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
934 light = getInteriorLight(ntop, 1, nodedef);
940 void MapblockMeshGenerator::drawFirelikeQuad(float rotation, float opening_angle,
941 float offset_h, float offset_v)
944 v3f(-scale, -BS / 2 + scale * 2, 0),
945 v3f( scale, -BS / 2 + scale * 2, 0),
946 v3f( scale, -BS / 2, 0),
947 v3f(-scale, -BS / 2, 0),
949 for (int i = 0; i < 4; i++) {
950 vertices[i].rotateYZBy(opening_angle);
951 vertices[i].Z += offset_h;
952 vertices[i].rotateXZBy(rotation);
953 vertices[i].Y += offset_v;
958 void MapblockMeshGenerator::drawFirelikeNode()
961 scale = BS / 2 * f->visual_scale;
963 // Check for adjacent nodes
964 bool neighbors = false;
965 bool neighbor[6] = {0, 0, 0, 0, 0, 0};
966 content_t current = n.getContent();
967 for (int i = 0; i < 6; i++) {
968 v3s16 n2p = blockpos_nodes + p + g_6dirs[i];
969 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
970 content_t n2c = n2.getContent();
971 if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
976 bool drawBasicFire = neighbor[D6D_YN] || !neighbors;
977 bool drawBottomFire = neighbor[D6D_YP];
979 if (drawBasicFire || neighbor[D6D_ZP])
980 drawFirelikeQuad(0, -10, 0.4 * BS);
981 else if (drawBottomFire)
982 drawFirelikeQuad(0, 70, 0.47 * BS, 0.484 * BS);
984 if (drawBasicFire || neighbor[D6D_XN])
985 drawFirelikeQuad(90, -10, 0.4 * BS);
986 else if (drawBottomFire)
987 drawFirelikeQuad(90, 70, 0.47 * BS, 0.484 * BS);
989 if (drawBasicFire || neighbor[D6D_ZN])
990 drawFirelikeQuad(180, -10, 0.4 * BS);
991 else if (drawBottomFire)
992 drawFirelikeQuad(180, 70, 0.47 * BS, 0.484 * BS);
994 if (drawBasicFire || neighbor[D6D_XP])
995 drawFirelikeQuad(270, -10, 0.4 * BS);
996 else if (drawBottomFire)
997 drawFirelikeQuad(270, 70, 0.47 * BS, 0.484 * BS);
1000 drawFirelikeQuad(45, 0, 0.0);
1001 drawFirelikeQuad(-45, 0, 0.0);
1005 void MapblockMeshGenerator::drawFencelikeNode()
1008 TileSpec tile_nocrack = tile;
1009 for (int layer = 0; layer < MAX_TILE_LAYERS; layer++)
1010 tile_nocrack.layers[layer].material_flags &= ~MATERIAL_FLAG_CRACK;
1012 // Put wood the right way around in the posts
1013 TileSpec tile_rot = tile;
1014 tile_rot.rotation = 1;
1016 static const f32 post_rad = BS / 8;
1017 static const f32 bar_rad = BS / 16;
1018 static const f32 bar_len = BS / 2 - post_rad;
1020 // The post - always present
1021 static const aabb3f post(-post_rad, -BS / 2, -post_rad,
1022 post_rad, BS / 2, post_rad);
1023 static const f32 postuv[24] = {
1024 0.375, 0.375, 0.625, 0.625,
1025 0.375, 0.375, 0.625, 0.625,
1026 0.000, 0.000, 0.250, 1.000,
1027 0.250, 0.000, 0.500, 1.000,
1028 0.500, 0.000, 0.750, 1.000,
1029 0.750, 0.000, 1.000, 1.000,
1032 drawAutoLightedCuboid(post, postuv);
1034 tile = tile_nocrack;
1036 // Now a section of fence, +X, if there's a post there
1039 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1040 const ContentFeatures *f2 = &nodedef->get(n2);
1041 if (f2->drawtype == NDT_FENCELIKE) {
1042 static const aabb3f bar_x1(BS / 2 - bar_len, BS / 4 - bar_rad, -bar_rad,
1043 BS / 2 + bar_len, BS / 4 + bar_rad, bar_rad);
1044 static const aabb3f bar_x2(BS / 2 - bar_len, -BS / 4 - bar_rad, -bar_rad,
1045 BS / 2 + bar_len, -BS / 4 + bar_rad, bar_rad);
1046 static const f32 xrailuv[24] = {
1047 0.000, 0.125, 1.000, 0.250,
1048 0.000, 0.250, 1.000, 0.375,
1049 0.375, 0.375, 0.500, 0.500,
1050 0.625, 0.625, 0.750, 0.750,
1051 0.000, 0.500, 1.000, 0.625,
1052 0.000, 0.875, 1.000, 1.000,
1054 drawAutoLightedCuboid(bar_x1, xrailuv);
1055 drawAutoLightedCuboid(bar_x2, xrailuv);
1058 // Now a section of fence, +Z, if there's a post there
1061 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1062 f2 = &nodedef->get(n2);
1063 if (f2->drawtype == NDT_FENCELIKE) {
1064 static const aabb3f bar_z1(-bar_rad, BS / 4 - bar_rad, BS / 2 - bar_len,
1065 bar_rad, BS / 4 + bar_rad, BS / 2 + bar_len);
1066 static const aabb3f bar_z2(-bar_rad, -BS / 4 - bar_rad, BS / 2 - bar_len,
1067 bar_rad, -BS / 4 + bar_rad, BS / 2 + bar_len);
1068 static const f32 zrailuv[24] = {
1069 0.1875, 0.0625, 0.3125, 0.3125, // cannot rotate; stretch
1070 0.2500, 0.0625, 0.3750, 0.3125, // for wood texture instead
1071 0.0000, 0.5625, 1.0000, 0.6875,
1072 0.0000, 0.3750, 1.0000, 0.5000,
1073 0.3750, 0.3750, 0.5000, 0.5000,
1074 0.6250, 0.6250, 0.7500, 0.7500,
1076 drawAutoLightedCuboid(bar_z1, zrailuv);
1077 drawAutoLightedCuboid(bar_z2, zrailuv);
1081 bool MapblockMeshGenerator::isSameRail(v3s16 dir)
1083 MapNode node2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
1084 if (node2.getContent() == n.getContent())
1086 const ContentFeatures &def2 = nodedef->get(node2);
1087 return ((def2.drawtype == NDT_RAILLIKE) &&
1088 (def2.getGroup(raillike_groupname) == raillike_group));
1091 void MapblockMeshGenerator::drawRaillikeNode()
1093 static const v3s16 direction[4] = {
1099 static const int slope_angle[4] = {0, 180, 90, -90};
1111 static const RailDesc rail_kinds[16] = {
1114 {straight, 0}, // . . . .
1115 {straight, 0}, // . . . +Z
1116 {straight, 0}, // . . -Z .
1117 {straight, 0}, // . . -Z +Z
1118 {straight, 90}, // . -X . .
1119 { curved, 180}, // . -X . +Z
1120 { curved, 270}, // . -X -Z .
1121 {junction, 180}, // . -X -Z +Z
1122 {straight, 90}, // +X . . .
1123 { curved, 90}, // +X . . +Z
1124 { curved, 0}, // +X . -Z .
1125 {junction, 0}, // +X . -Z +Z
1126 {straight, 90}, // +X -X . .
1127 {junction, 90}, // +X -X . +Z
1128 {junction, 270}, // +X -X -Z .
1129 { cross, 0}, // +X -X -Z +Z
1132 raillike_group = nodedef->get(n).getGroup(raillike_groupname);
1137 bool sloped = false;
1138 for (int dir = 0; dir < 4; dir++) {
1139 bool rail_above = isSameRail(direction[dir] + v3s16(0, 1, 0));
1142 angle = slope_angle[dir];
1145 isSameRail(direction[dir]) ||
1146 isSameRail(direction[dir] + v3s16(0, -1, 0)))
1151 tile_index = straight;
1153 tile_index = rail_kinds[code].tile_index;
1154 angle = rail_kinds[code].angle;
1157 useTile(tile_index, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
1159 static const float offset = BS / 64;
1160 static const float size = BS / 2;
1161 float y2 = sloped ? size : -size;
1163 v3f(-size, y2 + offset, size),
1164 v3f( size, y2 + offset, size),
1165 v3f( size, -size + offset, -size),
1166 v3f(-size, -size + offset, -size),
1169 for (int i = 0; i < 4; i++)
1170 vertices[i].rotateXZBy(angle);
1174 void MapblockMeshGenerator::drawNodeboxNode()
1176 static const v3s16 tile_dirs[6] = {
1185 // we have this order for some reason...
1186 static const v3s16 connection_dirs[6] = {
1187 v3s16( 0, 1, 0), // top
1188 v3s16( 0, -1, 0), // bottom
1189 v3s16( 0, 0, -1), // front
1190 v3s16(-1, 0, 0), // left
1191 v3s16( 0, 0, 1), // back
1192 v3s16( 1, 0, 0), // right
1196 for (int face = 0; face < 6; face++) {
1197 // Handles facedir rotation for textures
1198 getTile(tile_dirs[face], &tiles[face]);
1201 // locate possible neighboring nodes to connect to
1202 int neighbors_set = 0;
1203 if (f->node_box.type == NODEBOX_CONNECTED) {
1204 for (int dir = 0; dir != 6; dir++) {
1205 int flag = 1 << dir;
1206 v3s16 p2 = blockpos_nodes + p + connection_dirs[dir];
1207 MapNode n2 = data->m_vmanip.getNodeNoEx(p2);
1208 if (nodedef->nodeboxConnects(n, n2, flag))
1209 neighbors_set |= flag;
1213 std::vector<aabb3f> boxes;
1214 n.getNodeBoxes(nodedef, &boxes, neighbors_set);
1215 for (std::vector<aabb3f>::iterator i = boxes.begin(); i != boxes.end(); ++i)
1216 drawAutoLightedCuboid(*i, NULL, tiles, 6);
1219 void MapblockMeshGenerator::drawMeshNode()
1223 bool private_mesh; // as a grab/drop pair is not thread-safe
1225 if (f->param_type_2 == CPT2_FACEDIR ||
1226 f->param_type_2 == CPT2_COLORED_FACEDIR) {
1227 facedir = n.getFaceDir(nodedef);
1228 } else if (f->param_type_2 == CPT2_WALLMOUNTED ||
1229 f->param_type_2 == CPT2_COLORED_WALLMOUNTED) {
1230 // Convert wallmounted to 6dfacedir.
1231 // When cache enabled, it is already converted.
1232 facedir = n.getWallMounted(nodedef);
1233 if (!enable_mesh_cache) {
1234 static const u8 wm_to_6d[6] = {20, 0, 16 + 1, 12 + 3, 8, 4 + 2};
1235 facedir = wm_to_6d[facedir];
1239 if (!data->m_smooth_lighting && f->mesh_ptr[facedir]) {
1240 // use cached meshes
1241 private_mesh = false;
1242 mesh = f->mesh_ptr[facedir];
1243 } else if (f->mesh_ptr[0]) {
1244 // no cache, clone and rotate mesh
1245 private_mesh = true;
1246 mesh = cloneMesh(f->mesh_ptr[0]);
1247 rotateMeshBy6dFacedir(mesh, facedir);
1248 recalculateBoundingBox(mesh);
1249 meshmanip->recalculateNormals(mesh, true, false);
1253 int mesh_buffer_count = mesh->getMeshBufferCount();
1254 for (int j = 0; j < mesh_buffer_count; j++) {
1256 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1257 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1258 int vertex_count = buf->getVertexCount();
1260 if (data->m_smooth_lighting) {
1261 // Mesh is always private here. So the lighting is applied to each
1262 // vertex right here.
1263 for (int k = 0; k < vertex_count; k++) {
1264 video::S3DVertex &vertex = vertices[k];
1265 vertex.Color = blendLightColor(vertex.Pos, vertex.Normal);
1266 vertex.Pos += origin;
1268 collector->append(tile, vertices, vertex_count,
1269 buf->getIndices(), buf->getIndexCount());
1271 // Don't modify the mesh, it may not be private here.
1272 // Instead, let the collector process colors, etc.
1273 collector->append(tile, vertices, vertex_count,
1274 buf->getIndices(), buf->getIndexCount(), origin,
1275 color, f->light_source);
1282 // also called when the drawtype is known but should have been pre-converted
1283 void MapblockMeshGenerator::errorUnknownDrawtype()
1285 infostream << "Got drawtype " << f->drawtype << std::endl;
1286 FATAL_ERROR("Unknown drawtype");
1289 void MapblockMeshGenerator::drawNode()
1291 if (data->m_smooth_lighting)
1292 getSmoothLightFrame();
1294 light = getInteriorLight(n, 1, nodedef);
1295 switch (f->drawtype) {
1296 case NDT_NORMAL: break; // Drawn by MapBlockMesh
1297 case NDT_AIRLIKE: break; // Not drawn at all
1298 case NDT_LIQUID: break; // Drawn by MapBlockMesh
1299 case NDT_FLOWINGLIQUID: drawLiquidNode(); break;
1300 case NDT_GLASSLIKE: drawGlasslikeNode(); break;
1301 case NDT_GLASSLIKE_FRAMED: drawGlasslikeFramedNode(); break;
1302 case NDT_ALLFACES: drawAllfacesNode(); break;
1303 case NDT_TORCHLIKE: drawTorchlikeNode(); break;
1304 case NDT_SIGNLIKE: drawSignlikeNode(); break;
1305 case NDT_PLANTLIKE: drawPlantlikeNode(); break;
1306 case NDT_PLANTLIKE_ROOTED: drawPlantlikeRootedNode(); break;
1307 case NDT_FIRELIKE: drawFirelikeNode(); break;
1308 case NDT_FENCELIKE: drawFencelikeNode(); break;
1309 case NDT_RAILLIKE: drawRaillikeNode(); break;
1310 case NDT_NODEBOX: drawNodeboxNode(); break;
1311 case NDT_MESH: drawMeshNode(); break;
1312 default: errorUnknownDrawtype(); break;
1317 TODO: Fix alpha blending for special nodes
1318 Currently only the last element rendered is blended correct
1320 void MapblockMeshGenerator::generate()
1322 for (p.Z = 0; p.Z < MAP_BLOCKSIZE; p.Z++)
1323 for (p.Y = 0; p.Y < MAP_BLOCKSIZE; p.Y++)
1324 for (p.X = 0; p.X < MAP_BLOCKSIZE; p.X++) {
1325 n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1326 f = &nodedef->get(n);
1327 origin = intToFloat(p, BS);