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"
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 meshmanip = RenderingEngine::get_scene_manager()->getMeshManipulator();
69 enable_mesh_cache = g_settings->getBool("enable_mesh_cache") &&
70 !data->m_smooth_lighting; // Mesh cache is not supported with smooth lighting
72 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
75 void MapblockMeshGenerator::useTile(int index, u8 set_flags, u8 reset_flags, bool special)
78 getSpecialTile(index, &tile, p == data->m_crack_pos_relative);
80 getTile(index, &tile);
81 if (!data->m_smooth_lighting)
82 color = encode_light(light, f->light_source);
84 for (auto &layer : tile.layers) {
85 layer.material_flags |= set_flags;
86 layer.material_flags &= ~reset_flags;
90 // Returns a tile, ready for use, non-rotated.
91 void MapblockMeshGenerator::getTile(int index, TileSpec *tile)
93 getNodeTileN(n, p, index, data, *tile);
96 // Returns a tile, ready for use, rotated according to the node facedir.
97 void MapblockMeshGenerator::getTile(v3s16 direction, TileSpec *tile)
99 getNodeTile(n, p, direction, data, *tile);
102 // Returns a special tile, ready for use, non-rotated.
103 void MapblockMeshGenerator::getSpecialTile(int index, TileSpec *tile, bool apply_crack)
105 *tile = f->special_tiles[index];
106 TileLayer *top_layer = nullptr;
108 for (auto &layernum : tile->layers) {
109 TileLayer *layer = &layernum;
110 if (layer->texture_id == 0)
113 if (!layer->has_color)
114 n.getColor(*f, &layer->color);
118 top_layer->material_flags |= MATERIAL_FLAG_CRACK;
121 void MapblockMeshGenerator::drawQuad(v3f *coords, const v3s16 &normal,
122 float vertical_tiling)
124 const v2f tcoords[4] = {v2f(0.0, 0.0), v2f(1.0, 0.0),
125 v2f(1.0, vertical_tiling), v2f(0.0, vertical_tiling)};
126 video::S3DVertex vertices[4];
127 bool shade_face = !f->light_source && (normal != v3s16(0, 0, 0));
128 v3f normal2(normal.X, normal.Y, normal.Z);
129 for (int j = 0; j < 4; j++) {
130 vertices[j].Pos = coords[j] + origin;
131 vertices[j].Normal = normal2;
132 if (data->m_smooth_lighting)
133 vertices[j].Color = blendLightColor(coords[j]);
135 vertices[j].Color = color;
137 applyFacesShading(vertices[j].Color, normal2);
138 vertices[j].TCoords = tcoords[j];
140 collector->append(tile, vertices, 4, quad_indices, 6);
144 // tiles - the tiles (materials) to use (for all 6 faces)
145 // tilecount - number of entries in tiles, 1<=tilecount<=6
146 // lights - vertex light levels. The order is the same as in light_dirs.
147 // NULL may be passed if smooth lighting is disabled.
148 // txc - texture coordinates - this is a list of texture coordinates
149 // for the opposite corners of each face - therefore, there
150 // should be (2+2)*6=24 values in the list. The order of
151 // the faces in the list is up-down-right-left-back-front
152 // (compatible with ContentFeatures).
153 void MapblockMeshGenerator::drawCuboid(const aabb3f &box,
154 TileSpec *tiles, int tilecount, const LightPair *lights, const f32 *txc)
156 assert(tilecount >= 1 && tilecount <= 6); // pre-condition
158 v3f min = box.MinEdge;
159 v3f max = box.MaxEdge;
161 video::SColor colors[6];
162 if (!data->m_smooth_lighting) {
163 for (int face = 0; face != 6; ++face) {
164 colors[face] = encode_light(light, f->light_source);
166 if (!f->light_source) {
167 applyFacesShading(colors[0], v3f(0, 1, 0));
168 applyFacesShading(colors[1], v3f(0, -1, 0));
169 applyFacesShading(colors[2], v3f(1, 0, 0));
170 applyFacesShading(colors[3], v3f(-1, 0, 0));
171 applyFacesShading(colors[4], v3f(0, 0, 1));
172 applyFacesShading(colors[5], v3f(0, 0, -1));
176 video::S3DVertex vertices[24] = {
178 video::S3DVertex(min.X, max.Y, max.Z, 0, 1, 0, colors[0], txc[0], txc[1]),
179 video::S3DVertex(max.X, max.Y, max.Z, 0, 1, 0, colors[0], txc[2], txc[1]),
180 video::S3DVertex(max.X, max.Y, min.Z, 0, 1, 0, colors[0], txc[2], txc[3]),
181 video::S3DVertex(min.X, max.Y, min.Z, 0, 1, 0, colors[0], txc[0], txc[3]),
183 video::S3DVertex(min.X, min.Y, min.Z, 0, -1, 0, colors[1], txc[4], txc[5]),
184 video::S3DVertex(max.X, min.Y, min.Z, 0, -1, 0, colors[1], txc[6], txc[5]),
185 video::S3DVertex(max.X, min.Y, max.Z, 0, -1, 0, colors[1], txc[6], txc[7]),
186 video::S3DVertex(min.X, min.Y, max.Z, 0, -1, 0, colors[1], txc[4], txc[7]),
188 video::S3DVertex(max.X, max.Y, min.Z, 1, 0, 0, colors[2], txc[ 8], txc[9]),
189 video::S3DVertex(max.X, max.Y, max.Z, 1, 0, 0, colors[2], txc[10], txc[9]),
190 video::S3DVertex(max.X, min.Y, max.Z, 1, 0, 0, colors[2], txc[10], txc[11]),
191 video::S3DVertex(max.X, min.Y, min.Z, 1, 0, 0, colors[2], txc[ 8], txc[11]),
193 video::S3DVertex(min.X, max.Y, max.Z, -1, 0, 0, colors[3], txc[12], txc[13]),
194 video::S3DVertex(min.X, max.Y, min.Z, -1, 0, 0, colors[3], txc[14], txc[13]),
195 video::S3DVertex(min.X, min.Y, min.Z, -1, 0, 0, colors[3], txc[14], txc[15]),
196 video::S3DVertex(min.X, min.Y, max.Z, -1, 0, 0, colors[3], txc[12], txc[15]),
198 video::S3DVertex(max.X, max.Y, max.Z, 0, 0, 1, colors[4], txc[16], txc[17]),
199 video::S3DVertex(min.X, max.Y, max.Z, 0, 0, 1, colors[4], txc[18], txc[17]),
200 video::S3DVertex(min.X, min.Y, max.Z, 0, 0, 1, colors[4], txc[18], txc[19]),
201 video::S3DVertex(max.X, min.Y, max.Z, 0, 0, 1, colors[4], txc[16], txc[19]),
203 video::S3DVertex(min.X, max.Y, min.Z, 0, 0, -1, colors[5], txc[20], txc[21]),
204 video::S3DVertex(max.X, max.Y, min.Z, 0, 0, -1, colors[5], txc[22], txc[21]),
205 video::S3DVertex(max.X, min.Y, min.Z, 0, 0, -1, colors[5], txc[22], txc[23]),
206 video::S3DVertex(min.X, min.Y, min.Z, 0, 0, -1, colors[5], txc[20], txc[23]),
209 static const u8 light_indices[24] = {
218 for (int face = 0; face < 6; face++) {
219 int tileindex = MYMIN(face, tilecount - 1);
220 const TileSpec &tile = tiles[tileindex];
221 for (int j = 0; j < 4; j++) {
222 video::S3DVertex &vertex = vertices[face * 4 + j];
223 v2f &tcoords = vertex.TCoords;
224 switch (tile.rotation) {
228 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
231 tcoords.rotateBy(180, irr::core::vector2df(0, 0));
234 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
237 tcoords.X = 1.0 - tcoords.X;
238 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
241 tcoords.X = 1.0 - tcoords.X;
242 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
245 tcoords.Y = 1.0 - tcoords.Y;
246 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
249 tcoords.Y = 1.0 - tcoords.Y;
250 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
253 tcoords.X = 1.0 - tcoords.X;
256 tcoords.Y = 1.0 - tcoords.Y;
264 if (data->m_smooth_lighting) {
265 for (int j = 0; j < 24; ++j) {
266 vertices[j].Color = encode_light(lights[light_indices[j]],
268 if (!f->light_source)
269 applyFacesShading(vertices[j].Color, vertices[j].Normal);
273 // Add to mesh collector
274 for (int k = 0; k < 6; ++k) {
275 int tileindex = MYMIN(k, tilecount - 1);
276 collector->append(tiles[tileindex], vertices + 4 * k, 4, quad_indices, 6);
280 // Gets the base lighting values for a node
281 void MapblockMeshGenerator::getSmoothLightFrame()
283 for (int k = 0; k < 8; ++k) {
284 LightPair light(getSmoothLightTransparent(blockpos_nodes + p, light_dirs[k], data));
285 frame.lightsA[k] = light.lightA;
286 frame.lightsB[k] = light.lightB;
290 // Calculates vertex light level
291 // vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
292 LightPair MapblockMeshGenerator::blendLight(const v3f &vertex_pos)
294 f32 x = core::clamp(vertex_pos.X / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
295 f32 y = core::clamp(vertex_pos.Y / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
296 f32 z = core::clamp(vertex_pos.Z / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
299 for (int k = 0; k < 8; ++k) {
300 f32 dx = (k & 4) ? x : 1 - x;
301 f32 dy = (k & 2) ? y : 1 - y;
302 f32 dz = (k & 1) ? z : 1 - z;
303 lightA += dx * dy * dz * frame.lightsA[k];
304 lightB += dx * dy * dz * frame.lightsB[k];
306 return LightPair(lightA, lightB);
309 // Calculates vertex color to be used in mapblock mesh
310 // vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
311 // tile_color - node's tile color
312 video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos)
314 LightPair light = blendLight(vertex_pos);
315 return encode_light(light, f->light_source);
318 video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos,
319 const v3f &vertex_normal)
321 video::SColor color = blendLightColor(vertex_pos);
322 if (!f->light_source)
323 applyFacesShading(color, vertex_normal);
327 void MapblockMeshGenerator::generateCuboidTextureCoords(const aabb3f &box, f32 *coords)
329 f32 tx1 = (box.MinEdge.X / BS) + 0.5;
330 f32 ty1 = (box.MinEdge.Y / BS) + 0.5;
331 f32 tz1 = (box.MinEdge.Z / BS) + 0.5;
332 f32 tx2 = (box.MaxEdge.X / BS) + 0.5;
333 f32 ty2 = (box.MaxEdge.Y / BS) + 0.5;
334 f32 tz2 = (box.MaxEdge.Z / BS) + 0.5;
336 tx1, 1 - tz2, tx2, 1 - tz1, // up
337 tx1, tz1, tx2, tz2, // down
338 tz1, 1 - ty2, tz2, 1 - ty1, // right
339 1 - tz2, 1 - ty2, 1 - tz1, 1 - ty1, // left
340 1 - tx2, 1 - ty2, 1 - tx1, 1 - ty1, // back
341 tx1, 1 - ty2, tx2, 1 - ty1, // front
343 for (int i = 0; i != 24; ++i)
347 void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc,
348 TileSpec *tiles, int tile_count)
350 f32 texture_coord_buf[24];
351 f32 dx1 = box.MinEdge.X;
352 f32 dy1 = box.MinEdge.Y;
353 f32 dz1 = box.MinEdge.Z;
354 f32 dx2 = box.MaxEdge.X;
355 f32 dy2 = box.MaxEdge.Y;
356 f32 dz2 = box.MaxEdge.Z;
357 box.MinEdge += origin;
358 box.MaxEdge += origin;
360 generateCuboidTextureCoords(box, texture_coord_buf);
361 txc = texture_coord_buf;
367 if (data->m_smooth_lighting) {
369 for (int j = 0; j < 8; ++j) {
371 d.X = (j & 4) ? dx2 : dx1;
372 d.Y = (j & 2) ? dy2 : dy1;
373 d.Z = (j & 1) ? dz2 : dz1;
374 lights[j] = blendLight(d);
376 drawCuboid(box, tiles, tile_count, lights, txc);
378 drawCuboid(box, tiles, tile_count, nullptr, txc);
382 void MapblockMeshGenerator::prepareLiquidNodeDrawing()
384 getSpecialTile(0, &tile_liquid_top);
385 getSpecialTile(1, &tile_liquid);
387 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y + 1, p.Z));
388 MapNode nbottom = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y - 1, p.Z));
389 c_flowing = nodedef->getId(f->liquid_alternative_flowing);
390 c_source = nodedef->getId(f->liquid_alternative_source);
391 top_is_same_liquid = (ntop.getContent() == c_flowing) || (ntop.getContent() == c_source);
392 draw_liquid_bottom = (nbottom.getContent() != c_flowing) && (nbottom.getContent() != c_source);
393 if (draw_liquid_bottom) {
394 const ContentFeatures &f2 = nodedef->get(nbottom.getContent());
395 if (f2.solidness > 1)
396 draw_liquid_bottom = false;
399 if (data->m_smooth_lighting)
400 return; // don't need to pre-compute anything in this case
402 if (f->light_source != 0) {
403 // If this liquid emits light and doesn't contain light, draw
404 // it at what it emits, for an increased effect
405 u8 e = decode_light(f->light_source);
406 light = LightPair(std::max(e, light.lightA), std::max(e, light.lightB));
407 } else if (nodedef->get(ntop).param_type == CPT_LIGHT) {
408 // Otherwise, use the light of the node on top if possible
409 light = LightPair(getInteriorLight(ntop, 0, nodedef));
412 color_liquid_top = encode_light(light, f->light_source);
413 color = encode_light(light, f->light_source);
416 void MapblockMeshGenerator::getLiquidNeighborhood()
418 u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8);
420 for (int w = -1; w <= 1; w++)
421 for (int u = -1; u <= 1; u++) {
422 NeighborData &neighbor = liquid_neighbors[w + 1][u + 1];
423 v3s16 p2 = p + v3s16(u, 0, w);
424 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
425 neighbor.content = n2.getContent();
426 neighbor.level = -0.5 * BS;
427 neighbor.is_same_liquid = false;
428 neighbor.top_is_same_liquid = false;
430 if (neighbor.content == CONTENT_IGNORE)
433 if (neighbor.content == c_source) {
434 neighbor.is_same_liquid = true;
435 neighbor.level = 0.5 * BS;
436 } else if (neighbor.content == c_flowing) {
437 neighbor.is_same_liquid = true;
438 u8 liquid_level = (n2.param2 & LIQUID_LEVEL_MASK);
439 if (liquid_level <= LIQUID_LEVEL_MAX + 1 - range)
442 liquid_level -= (LIQUID_LEVEL_MAX + 1 - range);
443 neighbor.level = (-0.5 + (liquid_level + 0.5) / range) * BS;
446 // Check node above neighbor.
447 // NOTE: This doesn't get executed if neighbor
450 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
451 if (n2.getContent() == c_source || n2.getContent() == c_flowing)
452 neighbor.top_is_same_liquid = true;
456 void MapblockMeshGenerator::calculateCornerLevels()
458 for (int k = 0; k < 2; k++)
459 for (int i = 0; i < 2; i++)
460 corner_levels[k][i] = getCornerLevel(i, k);
463 f32 MapblockMeshGenerator::getCornerLevel(int i, int k)
468 for (int dk = 0; dk < 2; dk++)
469 for (int di = 0; di < 2; di++) {
470 NeighborData &neighbor_data = liquid_neighbors[k + dk][i + di];
471 content_t content = neighbor_data.content;
473 // If top is liquid, draw starting from top of node
474 if (neighbor_data.top_is_same_liquid)
477 // Source always has the full height
478 if (content == c_source)
481 // Flowing liquid has level information
482 if (content == c_flowing) {
483 sum += neighbor_data.level;
485 } else if (content == CONTENT_AIR) {
488 return -0.5 * BS + 0.2;
496 void MapblockMeshGenerator::drawLiquidSides()
498 struct LiquidFaceDesc {
500 v3s16 p[2]; // XZ only; 1 means +, 0 means -
505 static const LiquidFaceDesc base_faces[4] = {
506 {v3s16( 1, 0, 0), {v3s16(1, 0, 1), v3s16(1, 0, 0)}},
507 {v3s16(-1, 0, 0), {v3s16(0, 0, 0), v3s16(0, 0, 1)}},
508 {v3s16( 0, 0, 1), {v3s16(0, 0, 1), v3s16(1, 0, 1)}},
509 {v3s16( 0, 0, -1), {v3s16(1, 0, 0), v3s16(0, 0, 0)}},
511 static const UV base_vertices[4] = {
518 for (const auto &face : base_faces) {
519 const NeighborData &neighbor = liquid_neighbors[face.dir.Z + 1][face.dir.X + 1];
521 // No face between nodes of the same liquid, unless there is node
522 // at the top to which it should be connected. Again, unless the face
523 // there would be inside the liquid
524 if (neighbor.is_same_liquid) {
525 if (!top_is_same_liquid)
527 if (neighbor.top_is_same_liquid)
531 const ContentFeatures &neighbor_features = nodedef->get(neighbor.content);
532 // Don't draw face if neighbor is blocking the view
533 if (neighbor_features.solidness == 2)
536 video::S3DVertex vertices[4];
537 for (int j = 0; j < 4; j++) {
538 const UV &vertex = base_vertices[j];
539 const v3s16 &base = face.p[vertex.u];
541 pos.X = (base.X - 0.5) * BS;
542 pos.Z = (base.Z - 0.5) * BS;
544 pos.Y = neighbor.is_same_liquid ? corner_levels[base.Z][base.X] : -0.5 * BS;
546 pos.Y = !top_is_same_liquid ? corner_levels[base.Z][base.X] : 0.5 * BS;
547 if (data->m_smooth_lighting)
548 color = blendLightColor(pos);
550 vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, color, vertex.u, vertex.v);
552 collector->append(tile_liquid, vertices, 4, quad_indices, 6);
556 void MapblockMeshGenerator::drawLiquidTop()
558 // To get backface culling right, the vertices need to go
559 // clockwise around the front of the face. And we happened to
560 // calculate corner levels in exact reverse order.
561 static const int corner_resolve[4][2] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
563 video::S3DVertex vertices[4] = {
564 video::S3DVertex(-BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 0, 1),
565 video::S3DVertex( BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 1, 1),
566 video::S3DVertex( BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 1, 0),
567 video::S3DVertex(-BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 0, 0),
570 for (int i = 0; i < 4; i++) {
571 int u = corner_resolve[i][0];
572 int w = corner_resolve[i][1];
573 vertices[i].Pos.Y += corner_levels[w][u];
574 if (data->m_smooth_lighting)
575 vertices[i].Color = blendLightColor(vertices[i].Pos);
576 vertices[i].Pos += origin;
579 // Default downwards-flowing texture animation goes from
580 // -Z towards +Z, thus the direction is +Z.
581 // Rotate texture to make animation go in flow direction
582 // Positive if liquid moves towards +Z
583 f32 dz = (corner_levels[0][0] + corner_levels[0][1]) -
584 (corner_levels[1][0] + corner_levels[1][1]);
585 // Positive if liquid moves towards +X
586 f32 dx = (corner_levels[0][0] + corner_levels[1][0]) -
587 (corner_levels[0][1] + corner_levels[1][1]);
588 f32 tcoord_angle = atan2(dz, dx) * core::RADTODEG;
589 v2f tcoord_center(0.5, 0.5);
590 v2f tcoord_translate(blockpos_nodes.Z + p.Z, blockpos_nodes.X + p.X);
591 tcoord_translate.rotateBy(tcoord_angle);
592 tcoord_translate.X -= floor(tcoord_translate.X);
593 tcoord_translate.Y -= floor(tcoord_translate.Y);
595 for (video::S3DVertex &vertex : vertices) {
596 vertex.TCoords.rotateBy(tcoord_angle, tcoord_center);
597 vertex.TCoords += tcoord_translate;
600 std::swap(vertices[0].TCoords, vertices[2].TCoords);
602 collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
605 void MapblockMeshGenerator::drawLiquidBottom()
607 video::S3DVertex vertices[4] = {
608 video::S3DVertex(-BS / 2, -BS / 2, -BS / 2, 0, 0, 0, color_liquid_top, 0, 0),
609 video::S3DVertex( BS / 2, -BS / 2, -BS / 2, 0, 0, 0, color_liquid_top, 1, 0),
610 video::S3DVertex( BS / 2, -BS / 2, BS / 2, 0, 0, 0, color_liquid_top, 1, 1),
611 video::S3DVertex(-BS / 2, -BS / 2, BS / 2, 0, 0, 0, color_liquid_top, 0, 1),
614 for (int i = 0; i < 4; i++) {
615 if (data->m_smooth_lighting)
616 vertices[i].Color = blendLightColor(vertices[i].Pos);
617 vertices[i].Pos += origin;
620 collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
623 void MapblockMeshGenerator::drawLiquidNode()
625 prepareLiquidNodeDrawing();
626 getLiquidNeighborhood();
627 calculateCornerLevels();
629 if (!top_is_same_liquid)
631 if (draw_liquid_bottom)
635 void MapblockMeshGenerator::drawGlasslikeNode()
639 for (int face = 0; face < 6; face++) {
640 // Check this neighbor
641 v3s16 dir = g_6dirs[face];
642 v3s16 neighbor_pos = blockpos_nodes + p + dir;
643 MapNode neighbor = data->m_vmanip.getNodeNoExNoEmerge(neighbor_pos);
644 // Don't make face if neighbor is of same type
645 if (neighbor.getContent() == n.getContent())
649 v3f(-BS / 2, BS / 2, -BS / 2),
650 v3f( BS / 2, BS / 2, -BS / 2),
651 v3f( BS / 2, -BS / 2, -BS / 2),
652 v3f(-BS / 2, -BS / 2, -BS / 2),
655 for (v3f &vertex : vertices) {
658 vertex.rotateXZBy(180); break;
660 vertex.rotateYZBy( 90); break;
662 vertex.rotateXZBy( 90); break;
664 vertex.rotateXZBy( 0); break;
666 vertex.rotateYZBy(-90); break;
668 vertex.rotateXZBy(-90); break;
671 drawQuad(vertices, dir);
675 void MapblockMeshGenerator::drawGlasslikeFramedNode()
678 for (int face = 0; face < 6; face++)
679 getTile(g_6dirs[face], &tiles[face]);
681 TileSpec glass_tiles[6];
682 if (tiles[1].layers[0].texture &&
683 tiles[2].layers[0].texture &&
684 tiles[3].layers[0].texture) {
685 glass_tiles[0] = tiles[4];
686 glass_tiles[1] = tiles[0];
687 glass_tiles[2] = tiles[4];
688 glass_tiles[3] = tiles[4];
689 glass_tiles[4] = tiles[3];
690 glass_tiles[5] = tiles[4];
692 for (auto &glass_tile : glass_tiles)
693 glass_tile = tiles[4];
696 u8 param2 = n.getParam2();
697 bool H_merge = !(param2 & 128);
698 bool V_merge = !(param2 & 64);
701 static const float a = BS / 2;
702 static const float g = a - 0.003;
703 static const float b = .876 * ( BS / 2 );
705 static const aabb3f frame_edges[FRAMED_EDGE_COUNT] = {
706 aabb3f( b, b, -a, a, a, a), // y+
707 aabb3f(-a, b, -a, -b, a, a), // y+
708 aabb3f( b, -a, -a, a, -b, a), // y-
709 aabb3f(-a, -a, -a, -b, -b, a), // y-
710 aabb3f( b, -a, b, a, a, a), // x+
711 aabb3f( b, -a, -a, a, a, -b), // x+
712 aabb3f(-a, -a, b, -b, a, a), // x-
713 aabb3f(-a, -a, -a, -b, a, -b), // x-
714 aabb3f(-a, b, b, a, a, a), // z+
715 aabb3f(-a, -a, b, a, -b, a), // z+
716 aabb3f(-a, -a, -a, a, -b, -b), // z-
717 aabb3f(-a, b, -a, a, a, -b), // z-
719 static const aabb3f glass_faces[6] = {
720 aabb3f(-g, -g, g, g, g, g), // z+
721 aabb3f(-g, g, -g, g, g, g), // y+
722 aabb3f( g, -g, -g, g, g, g), // x+
723 aabb3f(-g, -g, -g, g, g, -g), // z-
724 aabb3f(-g, -g, -g, g, -g, g), // y-
725 aabb3f(-g, -g, -g, -g, g, g), // x-
728 // tables of neighbour (connect if same type and merge allowed),
729 // checked with g_26dirs
731 // 1 = connect, 0 = face visible
732 bool nb[FRAMED_NEIGHBOR_COUNT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
735 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};
736 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};
737 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};
738 const bool *check_nb = check_nb_all;
740 // neighbours checks for frames visibility
741 if (H_merge || V_merge) {
743 check_nb = check_nb_vertical; // vertical-only merge
745 check_nb = check_nb_horizontal; // horizontal-only merge
746 content_t current = n.getContent();
747 for (int i = 0; i < FRAMED_NEIGHBOR_COUNT; i++) {
750 v3s16 n2p = blockpos_nodes + p + g_26dirs[i];
751 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
752 content_t n2c = n2.getContent();
760 static const u8 nb_triplet[FRAMED_EDGE_COUNT][3] = {
761 {1, 2, 7}, {1, 5, 6}, {4, 2, 15}, {4, 5, 14},
762 {2, 0, 11}, {2, 3, 13}, {5, 0, 10}, {5, 3, 12},
763 {0, 1, 8}, {0, 4, 16}, {3, 4, 17}, {3, 1, 9},
767 for (int edge = 0; edge < FRAMED_EDGE_COUNT; edge++) {
769 if (nb[nb_triplet[edge][2]])
770 edge_invisible = nb[nb_triplet[edge][0]] & nb[nb_triplet[edge][1]];
772 edge_invisible = nb[nb_triplet[edge][0]] ^ nb[nb_triplet[edge][1]];
775 drawAutoLightedCuboid(frame_edges[edge]);
778 for (int face = 0; face < 6; face++) {
781 tile = glass_tiles[face];
782 drawAutoLightedCuboid(glass_faces[face]);
785 // Optionally render internal liquid level defined by param2
786 // Liquid is textured with 1 tile defined in nodedef 'special_tiles'
787 if (param2 > 0 && f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL &&
788 f->special_tiles[0].layers[0].texture) {
789 // Internal liquid level has param2 range 0 .. 63,
790 // convert it to -0.5 .. 0.5
791 float vlev = (param2 / 63.0) * 2.0 - 1.0;
792 getSpecialTile(0, &tile);
793 drawAutoLightedCuboid(aabb3f(-(nb[5] ? g : b),
797 (nb[1] ? g : b) * vlev,
802 void MapblockMeshGenerator::drawAllfacesNode()
804 static const aabb3f box(-BS / 2, -BS / 2, -BS / 2, BS / 2, BS / 2, BS / 2);
806 drawAutoLightedCuboid(box);
809 void MapblockMeshGenerator::drawTorchlikeNode()
811 u8 wall = n.getWallMounted(nodedef);
814 case DWM_YP: tileindex = 1; break; // ceiling
815 case DWM_YN: tileindex = 0; break; // floor
816 default: tileindex = 2; // side (or invalid—should we care?)
818 useTile(tileindex, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
820 float size = BS / 2 * f->visual_scale;
824 v3f( size, -size, 0),
825 v3f(-size, -size, 0),
828 for (v3f &vertex : vertices) {
831 vertex.rotateXZBy(-45); break;
833 vertex.rotateXZBy( 45); break;
835 vertex.rotateXZBy( 0); break;
837 vertex.rotateXZBy(180); break;
839 vertex.rotateXZBy( 90); break;
841 vertex.rotateXZBy(-90); break;
847 void MapblockMeshGenerator::drawSignlikeNode()
849 u8 wall = n.getWallMounted(nodedef);
850 useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
851 static const float offset = BS / 16;
852 float size = BS / 2 * f->visual_scale;
853 // Wall at X+ of node
855 v3f(BS / 2 - offset, size, size),
856 v3f(BS / 2 - offset, size, -size),
857 v3f(BS / 2 - offset, -size, -size),
858 v3f(BS / 2 - offset, -size, size),
861 for (v3f &vertex : vertices) {
864 vertex.rotateXYBy( 90); break;
866 vertex.rotateXYBy(-90); break;
868 vertex.rotateXZBy( 0); break;
870 vertex.rotateXZBy(180); break;
872 vertex.rotateXZBy( 90); break;
874 vertex.rotateXZBy(-90); break;
880 void MapblockMeshGenerator::drawPlantlikeQuad(float rotation, float quad_offset,
881 bool offset_top_only)
884 v3f(-scale, -BS / 2 + 2.0 * scale * plant_height, 0),
885 v3f( scale, -BS / 2 + 2.0 * scale * plant_height, 0),
886 v3f( scale, -BS / 2, 0),
887 v3f(-scale, -BS / 2, 0),
889 if (random_offset_Y) {
890 PseudoRandom yrng(face_num++ | p.X << 16 | p.Z << 8 | p.Y << 24);
891 offset.Y = -BS * ((yrng.next() % 16 / 16.0) * 0.125);
893 int offset_count = offset_top_only ? 2 : 4;
894 for (int i = 0; i < offset_count; i++)
895 vertices[i].Z += quad_offset;
897 for (v3f &vertex : vertices) {
898 vertex.rotateXZBy(rotation + rotate_degree);
901 drawQuad(vertices, v3s16(0, 0, 0), plant_height);
904 void MapblockMeshGenerator::drawPlantlike()
906 draw_style = PLANT_STYLE_CROSS;
907 scale = BS / 2 * f->visual_scale;
908 offset = v3f(0, 0, 0);
910 random_offset_Y = false;
914 switch (f->param_type_2) {
915 case CPT2_MESHOPTIONS:
916 draw_style = PlantlikeStyle(n.param2 & MO_MASK_STYLE);
917 if (n.param2 & MO_BIT_SCALE_SQRT2)
919 if (n.param2 & MO_BIT_RANDOM_OFFSET) {
920 PseudoRandom rng(p.X << 8 | p.Z | p.Y << 16);
921 offset.X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
922 offset.Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
924 if (n.param2 & MO_BIT_RANDOM_OFFSET_Y)
925 random_offset_Y = true;
929 rotate_degree = n.param2 * 2;
933 plant_height = n.param2 / 16.0;
940 switch (draw_style) {
941 case PLANT_STYLE_CROSS:
942 drawPlantlikeQuad(46);
943 drawPlantlikeQuad(-44);
946 case PLANT_STYLE_CROSS2:
947 drawPlantlikeQuad(91);
948 drawPlantlikeQuad(1);
951 case PLANT_STYLE_STAR:
952 drawPlantlikeQuad(121);
953 drawPlantlikeQuad(241);
954 drawPlantlikeQuad(1);
957 case PLANT_STYLE_HASH:
958 drawPlantlikeQuad( 1, BS / 4);
959 drawPlantlikeQuad( 91, BS / 4);
960 drawPlantlikeQuad(181, BS / 4);
961 drawPlantlikeQuad(271, BS / 4);
964 case PLANT_STYLE_HASH2:
965 drawPlantlikeQuad( 1, -BS / 2, true);
966 drawPlantlikeQuad( 91, -BS / 2, true);
967 drawPlantlikeQuad(181, -BS / 2, true);
968 drawPlantlikeQuad(271, -BS / 2, true);
973 void MapblockMeshGenerator::drawPlantlikeNode()
979 void MapblockMeshGenerator::drawPlantlikeRootedNode()
981 useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, 0, true);
982 origin += v3f(0.0, BS, 0.0);
984 if (data->m_smooth_lighting) {
985 getSmoothLightFrame();
987 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
988 light = LightPair(getInteriorLight(ntop, 1, nodedef));
994 void MapblockMeshGenerator::drawFirelikeQuad(float rotation, float opening_angle,
995 float offset_h, float offset_v)
998 v3f(-scale, -BS / 2 + scale * 2, 0),
999 v3f( scale, -BS / 2 + scale * 2, 0),
1000 v3f( scale, -BS / 2, 0),
1001 v3f(-scale, -BS / 2, 0),
1004 for (v3f &vertex : vertices) {
1005 vertex.rotateYZBy(opening_angle);
1006 vertex.Z += offset_h;
1007 vertex.rotateXZBy(rotation);
1008 vertex.Y += offset_v;
1013 void MapblockMeshGenerator::drawFirelikeNode()
1016 scale = BS / 2 * f->visual_scale;
1018 // Check for adjacent nodes
1019 bool neighbors = false;
1020 bool neighbor[6] = {0, 0, 0, 0, 0, 0};
1021 content_t current = n.getContent();
1022 for (int i = 0; i < 6; i++) {
1023 v3s16 n2p = blockpos_nodes + p + g_6dirs[i];
1024 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
1025 content_t n2c = n2.getContent();
1026 if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
1031 bool drawBasicFire = neighbor[D6D_YN] || !neighbors;
1032 bool drawBottomFire = neighbor[D6D_YP];
1034 if (drawBasicFire || neighbor[D6D_ZP])
1035 drawFirelikeQuad(0, -10, 0.4 * BS);
1036 else if (drawBottomFire)
1037 drawFirelikeQuad(0, 70, 0.47 * BS, 0.484 * BS);
1039 if (drawBasicFire || neighbor[D6D_XN])
1040 drawFirelikeQuad(90, -10, 0.4 * BS);
1041 else if (drawBottomFire)
1042 drawFirelikeQuad(90, 70, 0.47 * BS, 0.484 * BS);
1044 if (drawBasicFire || neighbor[D6D_ZN])
1045 drawFirelikeQuad(180, -10, 0.4 * BS);
1046 else if (drawBottomFire)
1047 drawFirelikeQuad(180, 70, 0.47 * BS, 0.484 * BS);
1049 if (drawBasicFire || neighbor[D6D_XP])
1050 drawFirelikeQuad(270, -10, 0.4 * BS);
1051 else if (drawBottomFire)
1052 drawFirelikeQuad(270, 70, 0.47 * BS, 0.484 * BS);
1054 if (drawBasicFire) {
1055 drawFirelikeQuad(45, 0, 0.0);
1056 drawFirelikeQuad(-45, 0, 0.0);
1060 void MapblockMeshGenerator::drawFencelikeNode()
1063 TileSpec tile_nocrack = tile;
1065 for (auto &layer : tile_nocrack.layers)
1066 layer.material_flags &= ~MATERIAL_FLAG_CRACK;
1068 // Put wood the right way around in the posts
1069 TileSpec tile_rot = tile;
1070 tile_rot.rotation = 1;
1072 static const f32 post_rad = BS / 8;
1073 static const f32 bar_rad = BS / 16;
1074 static const f32 bar_len = BS / 2 - post_rad;
1076 // The post - always present
1077 static const aabb3f post(-post_rad, -BS / 2, -post_rad,
1078 post_rad, BS / 2, post_rad);
1079 static const f32 postuv[24] = {
1080 0.375, 0.375, 0.625, 0.625,
1081 0.375, 0.375, 0.625, 0.625,
1082 0.000, 0.000, 0.250, 1.000,
1083 0.250, 0.000, 0.500, 1.000,
1084 0.500, 0.000, 0.750, 1.000,
1085 0.750, 0.000, 1.000, 1.000,
1088 drawAutoLightedCuboid(post, postuv);
1090 tile = tile_nocrack;
1092 // Now a section of fence, +X, if there's a post there
1095 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1096 const ContentFeatures *f2 = &nodedef->get(n2);
1097 if (f2->drawtype == NDT_FENCELIKE) {
1098 static const aabb3f bar_x1(BS / 2 - bar_len, BS / 4 - bar_rad, -bar_rad,
1099 BS / 2 + bar_len, BS / 4 + bar_rad, bar_rad);
1100 static const aabb3f bar_x2(BS / 2 - bar_len, -BS / 4 - bar_rad, -bar_rad,
1101 BS / 2 + bar_len, -BS / 4 + bar_rad, bar_rad);
1102 static const f32 xrailuv[24] = {
1103 0.000, 0.125, 1.000, 0.250,
1104 0.000, 0.250, 1.000, 0.375,
1105 0.375, 0.375, 0.500, 0.500,
1106 0.625, 0.625, 0.750, 0.750,
1107 0.000, 0.500, 1.000, 0.625,
1108 0.000, 0.875, 1.000, 1.000,
1110 drawAutoLightedCuboid(bar_x1, xrailuv);
1111 drawAutoLightedCuboid(bar_x2, xrailuv);
1114 // Now a section of fence, +Z, if there's a post there
1117 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1118 f2 = &nodedef->get(n2);
1119 if (f2->drawtype == NDT_FENCELIKE) {
1120 static const aabb3f bar_z1(-bar_rad, BS / 4 - bar_rad, BS / 2 - bar_len,
1121 bar_rad, BS / 4 + bar_rad, BS / 2 + bar_len);
1122 static const aabb3f bar_z2(-bar_rad, -BS / 4 - bar_rad, BS / 2 - bar_len,
1123 bar_rad, -BS / 4 + bar_rad, BS / 2 + bar_len);
1124 static const f32 zrailuv[24] = {
1125 0.1875, 0.0625, 0.3125, 0.3125, // cannot rotate; stretch
1126 0.2500, 0.0625, 0.3750, 0.3125, // for wood texture instead
1127 0.0000, 0.5625, 1.0000, 0.6875,
1128 0.0000, 0.3750, 1.0000, 0.5000,
1129 0.3750, 0.3750, 0.5000, 0.5000,
1130 0.6250, 0.6250, 0.7500, 0.7500,
1132 drawAutoLightedCuboid(bar_z1, zrailuv);
1133 drawAutoLightedCuboid(bar_z2, zrailuv);
1137 bool MapblockMeshGenerator::isSameRail(v3s16 dir)
1139 MapNode node2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
1140 if (node2.getContent() == n.getContent())
1142 const ContentFeatures &def2 = nodedef->get(node2);
1143 return ((def2.drawtype == NDT_RAILLIKE) &&
1144 (def2.getGroup(raillike_groupname) == raillike_group));
1147 void MapblockMeshGenerator::drawRaillikeNode()
1149 static const v3s16 direction[4] = {
1155 static const int slope_angle[4] = {0, 180, 90, -90};
1167 static const RailDesc rail_kinds[16] = {
1170 {straight, 0}, // . . . .
1171 {straight, 0}, // . . . +Z
1172 {straight, 0}, // . . -Z .
1173 {straight, 0}, // . . -Z +Z
1174 {straight, 90}, // . -X . .
1175 { curved, 180}, // . -X . +Z
1176 { curved, 270}, // . -X -Z .
1177 {junction, 180}, // . -X -Z +Z
1178 {straight, 90}, // +X . . .
1179 { curved, 90}, // +X . . +Z
1180 { curved, 0}, // +X . -Z .
1181 {junction, 0}, // +X . -Z +Z
1182 {straight, 90}, // +X -X . .
1183 {junction, 90}, // +X -X . +Z
1184 {junction, 270}, // +X -X -Z .
1185 { cross, 0}, // +X -X -Z +Z
1188 raillike_group = nodedef->get(n).getGroup(raillike_groupname);
1193 bool sloped = false;
1194 for (int dir = 0; dir < 4; dir++) {
1195 bool rail_above = isSameRail(direction[dir] + v3s16(0, 1, 0));
1198 angle = slope_angle[dir];
1201 isSameRail(direction[dir]) ||
1202 isSameRail(direction[dir] + v3s16(0, -1, 0)))
1207 tile_index = straight;
1209 tile_index = rail_kinds[code].tile_index;
1210 angle = rail_kinds[code].angle;
1213 useTile(tile_index, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
1215 static const float offset = BS / 64;
1216 static const float size = BS / 2;
1217 float y2 = sloped ? size : -size;
1219 v3f(-size, y2 + offset, size),
1220 v3f( size, y2 + offset, size),
1221 v3f( size, -size + offset, -size),
1222 v3f(-size, -size + offset, -size),
1225 for (v3f &vertex : vertices)
1226 vertex.rotateXZBy(angle);
1230 void MapblockMeshGenerator::drawNodeboxNode()
1232 static const v3s16 tile_dirs[6] = {
1241 // we have this order for some reason...
1242 static const v3s16 connection_dirs[6] = {
1243 v3s16( 0, 1, 0), // top
1244 v3s16( 0, -1, 0), // bottom
1245 v3s16( 0, 0, -1), // front
1246 v3s16(-1, 0, 0), // left
1247 v3s16( 0, 0, 1), // back
1248 v3s16( 1, 0, 0), // right
1252 for (int face = 0; face < 6; face++) {
1253 // Handles facedir rotation for textures
1254 getTile(tile_dirs[face], &tiles[face]);
1257 // locate possible neighboring nodes to connect to
1258 int neighbors_set = 0;
1259 if (f->node_box.type == NODEBOX_CONNECTED) {
1260 for (int dir = 0; dir != 6; dir++) {
1261 int flag = 1 << dir;
1262 v3s16 p2 = blockpos_nodes + p + connection_dirs[dir];
1263 MapNode n2 = data->m_vmanip.getNodeNoEx(p2);
1264 if (nodedef->nodeboxConnects(n, n2, flag))
1265 neighbors_set |= flag;
1269 std::vector<aabb3f> boxes;
1270 n.getNodeBoxes(nodedef, &boxes, neighbors_set);
1271 for (const auto &box : boxes)
1272 drawAutoLightedCuboid(box, nullptr, tiles, 6);
1275 void MapblockMeshGenerator::drawMeshNode()
1279 bool private_mesh; // as a grab/drop pair is not thread-safe
1281 if (f->param_type_2 == CPT2_FACEDIR ||
1282 f->param_type_2 == CPT2_COLORED_FACEDIR) {
1283 facedir = n.getFaceDir(nodedef);
1284 } else if (f->param_type_2 == CPT2_WALLMOUNTED ||
1285 f->param_type_2 == CPT2_COLORED_WALLMOUNTED) {
1286 // Convert wallmounted to 6dfacedir.
1287 // When cache enabled, it is already converted.
1288 facedir = n.getWallMounted(nodedef);
1289 if (!enable_mesh_cache)
1290 facedir = wallmounted_to_facedir[facedir];
1293 if (!data->m_smooth_lighting && f->mesh_ptr[facedir]) {
1294 // use cached meshes
1295 private_mesh = false;
1296 mesh = f->mesh_ptr[facedir];
1297 } else if (f->mesh_ptr[0]) {
1298 // no cache, clone and rotate mesh
1299 private_mesh = true;
1300 mesh = cloneMesh(f->mesh_ptr[0]);
1301 rotateMeshBy6dFacedir(mesh, facedir);
1302 recalculateBoundingBox(mesh);
1303 meshmanip->recalculateNormals(mesh, true, false);
1307 int mesh_buffer_count = mesh->getMeshBufferCount();
1308 for (int j = 0; j < mesh_buffer_count; j++) {
1310 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1311 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1312 int vertex_count = buf->getVertexCount();
1314 if (data->m_smooth_lighting) {
1315 // Mesh is always private here. So the lighting is applied to each
1316 // vertex right here.
1317 for (int k = 0; k < vertex_count; k++) {
1318 video::S3DVertex &vertex = vertices[k];
1319 vertex.Color = blendLightColor(vertex.Pos, vertex.Normal);
1320 vertex.Pos += origin;
1322 collector->append(tile, vertices, vertex_count,
1323 buf->getIndices(), buf->getIndexCount());
1325 // Don't modify the mesh, it may not be private here.
1326 // Instead, let the collector process colors, etc.
1327 collector->append(tile, vertices, vertex_count,
1328 buf->getIndices(), buf->getIndexCount(), origin,
1329 color, f->light_source);
1336 // also called when the drawtype is known but should have been pre-converted
1337 void MapblockMeshGenerator::errorUnknownDrawtype()
1339 infostream << "Got drawtype " << f->drawtype << std::endl;
1340 FATAL_ERROR("Unknown drawtype");
1343 void MapblockMeshGenerator::drawNode()
1345 // skip some drawtypes early
1346 switch (f->drawtype) {
1347 case NDT_NORMAL: // Drawn by MapBlockMesh
1348 case NDT_AIRLIKE: // Not drawn at all
1349 case NDT_LIQUID: // Drawn by MapBlockMesh
1354 origin = intToFloat(p, BS);
1355 if (data->m_smooth_lighting)
1356 getSmoothLightFrame();
1358 light = LightPair(getInteriorLight(n, 1, nodedef));
1359 switch (f->drawtype) {
1360 case NDT_FLOWINGLIQUID: drawLiquidNode(); break;
1361 case NDT_GLASSLIKE: drawGlasslikeNode(); break;
1362 case NDT_GLASSLIKE_FRAMED: drawGlasslikeFramedNode(); break;
1363 case NDT_ALLFACES: drawAllfacesNode(); break;
1364 case NDT_TORCHLIKE: drawTorchlikeNode(); break;
1365 case NDT_SIGNLIKE: drawSignlikeNode(); break;
1366 case NDT_PLANTLIKE: drawPlantlikeNode(); break;
1367 case NDT_PLANTLIKE_ROOTED: drawPlantlikeRootedNode(); break;
1368 case NDT_FIRELIKE: drawFirelikeNode(); break;
1369 case NDT_FENCELIKE: drawFencelikeNode(); break;
1370 case NDT_RAILLIKE: drawRaillikeNode(); break;
1371 case NDT_NODEBOX: drawNodeboxNode(); break;
1372 case NDT_MESH: drawMeshNode(); break;
1373 default: errorUnknownDrawtype(); break;
1378 TODO: Fix alpha blending for special nodes
1379 Currently only the last element rendered is blended correct
1381 void MapblockMeshGenerator::generate()
1383 for (p.Z = 0; p.Z < MAP_BLOCKSIZE; p.Z++)
1384 for (p.Y = 0; p.Y < MAP_BLOCKSIZE; p.Y++)
1385 for (p.X = 0; p.X < MAP_BLOCKSIZE; p.X++) {
1386 n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1387 f = &nodedef->get(n);
1392 void MapblockMeshGenerator::renderSingle(content_t node)
1395 n = MapNode(node, 0xff, 0x00);
1396 f = &nodedef->get(n);