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.
21 #include "content_mapblock.h"
22 #include "util/numeric.h"
23 #include "util/directiontables.h"
24 #include "mapblock_mesh.h"
27 #include "client/tile.h"
29 #include <IMeshManipulator.h>
30 #include "client/meshgen/collector.h"
31 #include "client/renderingengine.h"
35 // Distance of light extrapolation (for oversized nodes)
36 // After this distance, it gives up and considers light level constant
37 #define SMOOTH_LIGHTING_OVERSIZE 1.0
39 // Node edge count (for glasslike-framed)
40 #define FRAMED_EDGE_COUNT 12
42 // Node neighbor count, including edge-connected, but not vertex-connected
43 // (for glasslike-framed)
44 // Corresponding offsets are listed in g_27dirs
45 #define FRAMED_NEIGHBOR_COUNT 18
47 static const v3s16 light_dirs[8] = {
58 // Standard index set to make a quad on 4 vertices
59 static constexpr u16 quad_indices[] = {0, 1, 2, 2, 3, 0};
61 const std::string MapblockMeshGenerator::raillike_groupname = "connect_to_raillike";
63 MapblockMeshGenerator::MapblockMeshGenerator(MeshMakeData *input, MeshCollector *output)
68 nodedef = data->m_client->ndef();
69 meshmanip = RenderingEngine::get_scene_manager()->getMeshManipulator();
71 enable_mesh_cache = g_settings->getBool("enable_mesh_cache") &&
72 !data->m_smooth_lighting; // Mesh cache is not supported with smooth lighting
74 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
77 void MapblockMeshGenerator::useTile(int index, u8 set_flags, u8 reset_flags, bool special)
80 getSpecialTile(index, &tile, p == data->m_crack_pos_relative);
82 getTile(index, &tile);
83 if (!data->m_smooth_lighting)
84 color = encode_light(light, f->light_source);
86 for (auto &layer : tile.layers) {
87 layer.material_flags |= set_flags;
88 layer.material_flags &= ~reset_flags;
92 // Returns a tile, ready for use, non-rotated.
93 void MapblockMeshGenerator::getTile(int index, TileSpec *tile)
95 getNodeTileN(n, p, index, data, *tile);
98 // Returns a tile, ready for use, rotated according to the node facedir.
99 void MapblockMeshGenerator::getTile(v3s16 direction, TileSpec *tile)
101 getNodeTile(n, p, direction, data, *tile);
104 // Returns a special tile, ready for use, non-rotated.
105 void MapblockMeshGenerator::getSpecialTile(int index, TileSpec *tile, bool apply_crack)
107 *tile = f->special_tiles[index];
108 TileLayer *top_layer = nullptr;
110 for (auto &layernum : tile->layers) {
111 TileLayer *layer = &layernum;
112 if (layer->texture_id == 0)
115 if (!layer->has_color)
116 n.getColor(*f, &layer->color);
120 top_layer->material_flags |= MATERIAL_FLAG_CRACK;
123 void MapblockMeshGenerator::drawQuad(v3f *coords, const v3s16 &normal,
124 float vertical_tiling)
126 const v2f tcoords[4] = {v2f(0.0, 0.0), v2f(1.0, 0.0),
127 v2f(1.0, vertical_tiling), v2f(0.0, vertical_tiling)};
128 video::S3DVertex vertices[4];
129 bool shade_face = !f->light_source && (normal != v3s16(0, 0, 0));
130 v3f normal2(normal.X, normal.Y, normal.Z);
131 for (int j = 0; j < 4; j++) {
132 vertices[j].Pos = coords[j] + origin;
133 vertices[j].Normal = normal2;
134 if (data->m_smooth_lighting)
135 vertices[j].Color = blendLightColor(coords[j]);
137 vertices[j].Color = color;
139 applyFacesShading(vertices[j].Color, normal2);
140 vertices[j].TCoords = tcoords[j];
142 collector->append(tile, vertices, 4, quad_indices, 6);
146 // tiles - the tiles (materials) to use (for all 6 faces)
147 // tilecount - number of entries in tiles, 1<=tilecount<=6
148 // lights - vertex light levels. The order is the same as in light_dirs.
149 // NULL may be passed if smooth lighting is disabled.
150 // txc - texture coordinates - this is a list of texture coordinates
151 // for the opposite corners of each face - therefore, there
152 // should be (2+2)*6=24 values in the list. The order of
153 // the faces in the list is up-down-right-left-back-front
154 // (compatible with ContentFeatures).
155 void MapblockMeshGenerator::drawCuboid(const aabb3f &box,
156 TileSpec *tiles, int tilecount, const LightInfo *lights, const f32 *txc)
158 assert(tilecount >= 1 && tilecount <= 6); // pre-condition
160 v3f min = box.MinEdge;
161 v3f max = box.MaxEdge;
163 video::SColor colors[6];
164 if (!data->m_smooth_lighting) {
165 for (int face = 0; face != 6; ++face) {
166 colors[face] = encode_light(light, f->light_source);
168 if (!f->light_source) {
169 applyFacesShading(colors[0], v3f(0, 1, 0));
170 applyFacesShading(colors[1], v3f(0, -1, 0));
171 applyFacesShading(colors[2], v3f(1, 0, 0));
172 applyFacesShading(colors[3], v3f(-1, 0, 0));
173 applyFacesShading(colors[4], v3f(0, 0, 1));
174 applyFacesShading(colors[5], v3f(0, 0, -1));
178 video::S3DVertex vertices[24] = {
180 video::S3DVertex(min.X, max.Y, max.Z, 0, 1, 0, colors[0], txc[0], txc[1]),
181 video::S3DVertex(max.X, max.Y, max.Z, 0, 1, 0, colors[0], txc[2], txc[1]),
182 video::S3DVertex(max.X, max.Y, min.Z, 0, 1, 0, colors[0], txc[2], txc[3]),
183 video::S3DVertex(min.X, max.Y, min.Z, 0, 1, 0, colors[0], txc[0], txc[3]),
185 video::S3DVertex(min.X, min.Y, min.Z, 0, -1, 0, colors[1], txc[4], txc[5]),
186 video::S3DVertex(max.X, min.Y, min.Z, 0, -1, 0, colors[1], txc[6], txc[5]),
187 video::S3DVertex(max.X, min.Y, max.Z, 0, -1, 0, colors[1], txc[6], txc[7]),
188 video::S3DVertex(min.X, min.Y, max.Z, 0, -1, 0, colors[1], txc[4], txc[7]),
190 video::S3DVertex(max.X, max.Y, min.Z, 1, 0, 0, colors[2], txc[ 8], txc[9]),
191 video::S3DVertex(max.X, max.Y, max.Z, 1, 0, 0, colors[2], txc[10], txc[9]),
192 video::S3DVertex(max.X, min.Y, max.Z, 1, 0, 0, colors[2], txc[10], txc[11]),
193 video::S3DVertex(max.X, min.Y, min.Z, 1, 0, 0, colors[2], txc[ 8], txc[11]),
195 video::S3DVertex(min.X, max.Y, max.Z, -1, 0, 0, colors[3], txc[12], txc[13]),
196 video::S3DVertex(min.X, max.Y, min.Z, -1, 0, 0, colors[3], txc[14], txc[13]),
197 video::S3DVertex(min.X, min.Y, min.Z, -1, 0, 0, colors[3], txc[14], txc[15]),
198 video::S3DVertex(min.X, min.Y, max.Z, -1, 0, 0, colors[3], txc[12], txc[15]),
200 video::S3DVertex(max.X, max.Y, max.Z, 0, 0, 1, colors[4], txc[16], txc[17]),
201 video::S3DVertex(min.X, max.Y, max.Z, 0, 0, 1, colors[4], txc[18], txc[17]),
202 video::S3DVertex(min.X, min.Y, max.Z, 0, 0, 1, colors[4], txc[18], txc[19]),
203 video::S3DVertex(max.X, min.Y, max.Z, 0, 0, 1, colors[4], txc[16], txc[19]),
205 video::S3DVertex(min.X, max.Y, min.Z, 0, 0, -1, colors[5], txc[20], txc[21]),
206 video::S3DVertex(max.X, max.Y, min.Z, 0, 0, -1, colors[5], txc[22], txc[21]),
207 video::S3DVertex(max.X, min.Y, min.Z, 0, 0, -1, colors[5], txc[22], txc[23]),
208 video::S3DVertex(min.X, min.Y, min.Z, 0, 0, -1, colors[5], txc[20], txc[23]),
211 static const u8 light_indices[24] = {
220 for (int face = 0; face < 6; face++) {
221 int tileindex = MYMIN(face, tilecount - 1);
222 const TileSpec &tile = tiles[tileindex];
223 for (int j = 0; j < 4; j++) {
224 video::S3DVertex &vertex = vertices[face * 4 + j];
225 v2f &tcoords = vertex.TCoords;
226 switch (tile.rotation) {
230 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
233 tcoords.rotateBy(180, irr::core::vector2df(0, 0));
236 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
239 tcoords.X = 1.0 - tcoords.X;
240 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
243 tcoords.X = 1.0 - tcoords.X;
244 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
247 tcoords.Y = 1.0 - tcoords.Y;
248 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
251 tcoords.Y = 1.0 - tcoords.Y;
252 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
255 tcoords.X = 1.0 - tcoords.X;
258 tcoords.Y = 1.0 - tcoords.Y;
266 if (data->m_smooth_lighting) {
267 for (int j = 0; j < 24; ++j) {
268 video::S3DVertex &vertex = vertices[j];
269 vertex.Color = encode_light(
270 lights[light_indices[j]].getPair(MYMAX(0.0f, vertex.Normal.Y)),
272 if (!f->light_source)
273 applyFacesShading(vertex.Color, vertex.Normal);
277 // Add to mesh collector
278 for (int k = 0; k < 6; ++k) {
279 int tileindex = MYMIN(k, tilecount - 1);
280 collector->append(tiles[tileindex], vertices + 4 * k, 4, quad_indices, 6);
284 // Gets the base lighting values for a node
285 void MapblockMeshGenerator::getSmoothLightFrame()
287 for (int k = 0; k < 8; ++k)
288 frame.sunlight[k] = false;
289 for (int k = 0; k < 8; ++k) {
290 LightPair light(getSmoothLightTransparent(blockpos_nodes + p, light_dirs[k], data));
291 frame.lightsDay[k] = light.lightDay;
292 frame.lightsNight[k] = light.lightNight;
293 // If there is direct sunlight and no ambient occlusion at some corner,
294 // mark the vertical edge (top and bottom corners) containing it.
295 if (light.lightDay == 255) {
296 frame.sunlight[k] = true;
297 frame.sunlight[k ^ 2] = true;
302 // Calculates vertex light level
303 // vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
304 LightInfo MapblockMeshGenerator::blendLight(const v3f &vertex_pos)
306 // Light levels at (logical) node corners are known. Here,
307 // trilinear interpolation is used to calculate light level
308 // at a given point in the node.
309 f32 x = core::clamp(vertex_pos.X / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
310 f32 y = core::clamp(vertex_pos.Y / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
311 f32 z = core::clamp(vertex_pos.Z / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
312 f32 lightDay = 0.0; // daylight
313 f32 lightNight = 0.0;
314 f32 lightBoosted = 0.0; // daylight + direct sunlight, if any
315 for (int k = 0; k < 8; ++k) {
316 f32 dx = (k & 4) ? x : 1 - x;
317 f32 dy = (k & 2) ? y : 1 - y;
318 f32 dz = (k & 1) ? z : 1 - z;
319 // Use direct sunlight (255), if any; use daylight otherwise.
320 f32 light_boosted = frame.sunlight[k] ? 255 : frame.lightsDay[k];
321 lightDay += dx * dy * dz * frame.lightsDay[k];
322 lightNight += dx * dy * dz * frame.lightsNight[k];
323 lightBoosted += dx * dy * dz * light_boosted;
325 return LightInfo{lightDay, lightNight, lightBoosted};
328 // Calculates vertex color to be used in mapblock mesh
329 // vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
330 // tile_color - node's tile color
331 video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos)
333 LightInfo light = blendLight(vertex_pos);
334 return encode_light(light.getPair(), f->light_source);
337 video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos,
338 const v3f &vertex_normal)
340 LightInfo light = blendLight(vertex_pos);
341 video::SColor color = encode_light(light.getPair(MYMAX(0.0f, vertex_normal.Y)), f->light_source);
342 if (!f->light_source)
343 applyFacesShading(color, vertex_normal);
347 void MapblockMeshGenerator::generateCuboidTextureCoords(const aabb3f &box, f32 *coords)
349 f32 tx1 = (box.MinEdge.X / BS) + 0.5;
350 f32 ty1 = (box.MinEdge.Y / BS) + 0.5;
351 f32 tz1 = (box.MinEdge.Z / BS) + 0.5;
352 f32 tx2 = (box.MaxEdge.X / BS) + 0.5;
353 f32 ty2 = (box.MaxEdge.Y / BS) + 0.5;
354 f32 tz2 = (box.MaxEdge.Z / BS) + 0.5;
356 tx1, 1 - tz2, tx2, 1 - tz1, // up
357 tx1, tz1, tx2, tz2, // down
358 tz1, 1 - ty2, tz2, 1 - ty1, // right
359 1 - tz2, 1 - ty2, 1 - tz1, 1 - ty1, // left
360 1 - tx2, 1 - ty2, 1 - tx1, 1 - ty1, // back
361 tx1, 1 - ty2, tx2, 1 - ty1, // front
363 for (int i = 0; i != 24; ++i)
367 void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc,
368 TileSpec *tiles, int tile_count)
370 bool scale = std::fabs(f->visual_scale - 1.0f) > 1e-3f;
371 f32 texture_coord_buf[24];
372 f32 dx1 = box.MinEdge.X;
373 f32 dy1 = box.MinEdge.Y;
374 f32 dz1 = box.MinEdge.Z;
375 f32 dx2 = box.MaxEdge.X;
376 f32 dy2 = box.MaxEdge.Y;
377 f32 dz2 = box.MaxEdge.Z;
379 if (!txc) { // generate texture coords before scaling
380 generateCuboidTextureCoords(box, texture_coord_buf);
381 txc = texture_coord_buf;
383 box.MinEdge *= f->visual_scale;
384 box.MaxEdge *= f->visual_scale;
386 box.MinEdge += origin;
387 box.MaxEdge += origin;
389 generateCuboidTextureCoords(box, texture_coord_buf);
390 txc = texture_coord_buf;
396 if (data->m_smooth_lighting) {
398 for (int j = 0; j < 8; ++j) {
400 d.X = (j & 4) ? dx2 : dx1;
401 d.Y = (j & 2) ? dy2 : dy1;
402 d.Z = (j & 1) ? dz2 : dz1;
403 lights[j] = blendLight(d);
405 drawCuboid(box, tiles, tile_count, lights, txc);
407 drawCuboid(box, tiles, tile_count, nullptr, txc);
411 void MapblockMeshGenerator::prepareLiquidNodeDrawing()
413 getSpecialTile(0, &tile_liquid_top);
414 getSpecialTile(1, &tile_liquid);
416 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y + 1, p.Z));
417 MapNode nbottom = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y - 1, p.Z));
418 c_flowing = f->liquid_alternative_flowing_id;
419 c_source = f->liquid_alternative_source_id;
420 top_is_same_liquid = (ntop.getContent() == c_flowing) || (ntop.getContent() == c_source);
421 draw_liquid_bottom = (nbottom.getContent() != c_flowing) && (nbottom.getContent() != c_source);
422 if (draw_liquid_bottom) {
423 const ContentFeatures &f2 = nodedef->get(nbottom.getContent());
424 if (f2.solidness > 1)
425 draw_liquid_bottom = false;
428 if (data->m_smooth_lighting)
429 return; // don't need to pre-compute anything in this case
431 if (f->light_source != 0) {
432 // If this liquid emits light and doesn't contain light, draw
433 // it at what it emits, for an increased effect
434 u8 e = decode_light(f->light_source);
435 light = LightPair(std::max(e, light.lightDay), std::max(e, light.lightNight));
436 } else if (nodedef->get(ntop).param_type == CPT_LIGHT) {
437 // Otherwise, use the light of the node on top if possible
438 light = LightPair(getInteriorLight(ntop, 0, nodedef));
441 color_liquid_top = encode_light(light, f->light_source);
442 color = encode_light(light, f->light_source);
445 void MapblockMeshGenerator::getLiquidNeighborhood()
447 u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8);
449 for (int w = -1; w <= 1; w++)
450 for (int u = -1; u <= 1; u++) {
451 NeighborData &neighbor = liquid_neighbors[w + 1][u + 1];
452 v3s16 p2 = p + v3s16(u, 0, w);
453 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
454 neighbor.content = n2.getContent();
455 neighbor.level = -0.5 * BS;
456 neighbor.is_same_liquid = false;
457 neighbor.top_is_same_liquid = false;
459 if (neighbor.content == CONTENT_IGNORE)
462 if (neighbor.content == c_source) {
463 neighbor.is_same_liquid = true;
464 neighbor.level = 0.5 * BS;
465 } else if (neighbor.content == c_flowing) {
466 neighbor.is_same_liquid = true;
467 u8 liquid_level = (n2.param2 & LIQUID_LEVEL_MASK);
468 if (liquid_level <= LIQUID_LEVEL_MAX + 1 - range)
471 liquid_level -= (LIQUID_LEVEL_MAX + 1 - range);
472 neighbor.level = (-0.5 + (liquid_level + 0.5) / range) * BS;
475 // Check node above neighbor.
476 // NOTE: This doesn't get executed if neighbor
479 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
480 if (n2.getContent() == c_source || n2.getContent() == c_flowing)
481 neighbor.top_is_same_liquid = true;
485 void MapblockMeshGenerator::calculateCornerLevels()
487 for (int k = 0; k < 2; k++)
488 for (int i = 0; i < 2; i++)
489 corner_levels[k][i] = getCornerLevel(i, k);
492 f32 MapblockMeshGenerator::getCornerLevel(int i, int k)
497 for (int dk = 0; dk < 2; dk++)
498 for (int di = 0; di < 2; di++) {
499 NeighborData &neighbor_data = liquid_neighbors[k + dk][i + di];
500 content_t content = neighbor_data.content;
502 // If top is liquid, draw starting from top of node
503 if (neighbor_data.top_is_same_liquid)
506 // Source always has the full height
507 if (content == c_source)
510 // Flowing liquid has level information
511 if (content == c_flowing) {
512 sum += neighbor_data.level;
514 } else if (content == CONTENT_AIR) {
517 return -0.5 * BS + 0.2;
526 struct LiquidFaceDesc {
528 v3s16 p[2]; // XZ only; 1 means +, 0 means -
533 static const LiquidFaceDesc liquid_base_faces[4] = {
534 {v3s16( 1, 0, 0), {v3s16(1, 0, 1), v3s16(1, 0, 0)}},
535 {v3s16(-1, 0, 0), {v3s16(0, 0, 0), v3s16(0, 0, 1)}},
536 {v3s16( 0, 0, 1), {v3s16(0, 0, 1), v3s16(1, 0, 1)}},
537 {v3s16( 0, 0, -1), {v3s16(1, 0, 0), v3s16(0, 0, 0)}},
539 static const UV liquid_base_vertices[4] = {
547 void MapblockMeshGenerator::drawLiquidSides()
549 for (const auto &face : liquid_base_faces) {
550 const NeighborData &neighbor = liquid_neighbors[face.dir.Z + 1][face.dir.X + 1];
552 // No face between nodes of the same liquid, unless there is node
553 // at the top to which it should be connected. Again, unless the face
554 // there would be inside the liquid
555 if (neighbor.is_same_liquid) {
556 if (!top_is_same_liquid)
558 if (neighbor.top_is_same_liquid)
562 const ContentFeatures &neighbor_features = nodedef->get(neighbor.content);
563 // Don't draw face if neighbor is blocking the view
564 if (neighbor_features.solidness == 2)
567 video::S3DVertex vertices[4];
568 for (int j = 0; j < 4; j++) {
569 const UV &vertex = liquid_base_vertices[j];
570 const v3s16 &base = face.p[vertex.u];
574 pos.X = (base.X - 0.5f) * BS;
575 pos.Z = (base.Z - 0.5f) * BS;
577 pos.Y = neighbor.is_same_liquid ? corner_levels[base.Z][base.X] : -0.5f * BS;
578 } else if (top_is_same_liquid) {
581 pos.Y = corner_levels[base.Z][base.X];
582 v += (0.5f * BS - corner_levels[base.Z][base.X]) / BS;
585 if (data->m_smooth_lighting)
586 color = blendLightColor(pos);
588 vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, color, vertex.u, v);
590 collector->append(tile_liquid, vertices, 4, quad_indices, 6);
594 void MapblockMeshGenerator::drawLiquidTop()
596 // To get backface culling right, the vertices need to go
597 // clockwise around the front of the face. And we happened to
598 // calculate corner levels in exact reverse order.
599 static const int corner_resolve[4][2] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
601 video::S3DVertex vertices[4] = {
602 video::S3DVertex(-BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 0, 1),
603 video::S3DVertex( BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 1, 1),
604 video::S3DVertex( BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 1, 0),
605 video::S3DVertex(-BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 0, 0),
608 for (int i = 0; i < 4; i++) {
609 int u = corner_resolve[i][0];
610 int w = corner_resolve[i][1];
611 vertices[i].Pos.Y += corner_levels[w][u];
612 if (data->m_smooth_lighting)
613 vertices[i].Color = blendLightColor(vertices[i].Pos);
614 vertices[i].Pos += origin;
617 // Default downwards-flowing texture animation goes from
618 // -Z towards +Z, thus the direction is +Z.
619 // Rotate texture to make animation go in flow direction
620 // Positive if liquid moves towards +Z
621 f32 dz = (corner_levels[0][0] + corner_levels[0][1]) -
622 (corner_levels[1][0] + corner_levels[1][1]);
623 // Positive if liquid moves towards +X
624 f32 dx = (corner_levels[0][0] + corner_levels[1][0]) -
625 (corner_levels[0][1] + corner_levels[1][1]);
626 f32 tcoord_angle = atan2(dz, dx) * core::RADTODEG;
627 v2f tcoord_center(0.5, 0.5);
628 v2f tcoord_translate(blockpos_nodes.Z + p.Z, blockpos_nodes.X + p.X);
629 tcoord_translate.rotateBy(tcoord_angle);
630 tcoord_translate.X -= floor(tcoord_translate.X);
631 tcoord_translate.Y -= floor(tcoord_translate.Y);
633 for (video::S3DVertex &vertex : vertices) {
634 vertex.TCoords.rotateBy(tcoord_angle, tcoord_center);
635 vertex.TCoords += tcoord_translate;
638 std::swap(vertices[0].TCoords, vertices[2].TCoords);
640 collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
643 void MapblockMeshGenerator::drawLiquidBottom()
645 video::S3DVertex vertices[4] = {
646 video::S3DVertex(-BS / 2, -BS / 2, -BS / 2, 0, 0, 0, color_liquid_top, 0, 0),
647 video::S3DVertex( BS / 2, -BS / 2, -BS / 2, 0, 0, 0, color_liquid_top, 1, 0),
648 video::S3DVertex( BS / 2, -BS / 2, BS / 2, 0, 0, 0, color_liquid_top, 1, 1),
649 video::S3DVertex(-BS / 2, -BS / 2, BS / 2, 0, 0, 0, color_liquid_top, 0, 1),
652 for (int i = 0; i < 4; i++) {
653 if (data->m_smooth_lighting)
654 vertices[i].Color = blendLightColor(vertices[i].Pos);
655 vertices[i].Pos += origin;
658 collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
661 void MapblockMeshGenerator::drawLiquidNode()
663 prepareLiquidNodeDrawing();
664 getLiquidNeighborhood();
665 calculateCornerLevels();
667 if (!top_is_same_liquid)
669 if (draw_liquid_bottom)
673 void MapblockMeshGenerator::drawGlasslikeNode()
677 for (int face = 0; face < 6; face++) {
678 // Check this neighbor
679 v3s16 dir = g_6dirs[face];
680 v3s16 neighbor_pos = blockpos_nodes + p + dir;
681 MapNode neighbor = data->m_vmanip.getNodeNoExNoEmerge(neighbor_pos);
682 // Don't make face if neighbor is of same type
683 if (neighbor.getContent() == n.getContent())
687 v3f(-BS / 2, BS / 2, -BS / 2),
688 v3f( BS / 2, BS / 2, -BS / 2),
689 v3f( BS / 2, -BS / 2, -BS / 2),
690 v3f(-BS / 2, -BS / 2, -BS / 2),
693 for (v3f &vertex : vertices) {
696 vertex.rotateXZBy(180); break;
698 vertex.rotateYZBy( 90); break;
700 vertex.rotateXZBy( 90); break;
702 vertex.rotateXZBy( 0); break;
704 vertex.rotateYZBy(-90); break;
706 vertex.rotateXZBy(-90); break;
709 drawQuad(vertices, dir);
713 void MapblockMeshGenerator::drawGlasslikeFramedNode()
716 for (int face = 0; face < 6; face++)
717 getTile(g_6dirs[face], &tiles[face]);
719 if (!data->m_smooth_lighting)
720 color = encode_light(light, f->light_source);
722 TileSpec glass_tiles[6];
723 for (auto &glass_tile : glass_tiles)
724 glass_tile = tiles[4];
726 u8 param2 = n.getParam2();
727 bool H_merge = !(param2 & 128);
728 bool V_merge = !(param2 & 64);
731 static const float a = BS / 2.0f;
732 static const float g = a - 0.03f;
733 static const float b = 0.876f * (BS / 2.0f);
735 static const aabb3f frame_edges[FRAMED_EDGE_COUNT] = {
736 aabb3f( b, b, -a, a, a, a), // y+
737 aabb3f(-a, b, -a, -b, a, a), // y+
738 aabb3f( b, -a, -a, a, -b, a), // y-
739 aabb3f(-a, -a, -a, -b, -b, a), // y-
740 aabb3f( b, -a, b, a, a, a), // x+
741 aabb3f( b, -a, -a, a, a, -b), // x+
742 aabb3f(-a, -a, b, -b, a, a), // x-
743 aabb3f(-a, -a, -a, -b, a, -b), // x-
744 aabb3f(-a, b, b, a, a, a), // z+
745 aabb3f(-a, -a, b, a, -b, a), // z+
746 aabb3f(-a, -a, -a, a, -b, -b), // z-
747 aabb3f(-a, b, -a, a, a, -b), // z-
750 // tables of neighbour (connect if same type and merge allowed),
751 // checked with g_26dirs
753 // 1 = connect, 0 = face visible
754 bool nb[FRAMED_NEIGHBOR_COUNT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
757 static const bool check_nb_vertical [FRAMED_NEIGHBOR_COUNT] =
758 {0,1,0,0,1,0, 0,0,0,0, 0,0,0,0, 0,0,0,0};
759 static const bool check_nb_horizontal [FRAMED_NEIGHBOR_COUNT] =
760 {1,0,1,1,0,1, 0,0,0,0, 1,1,1,1, 0,0,0,0};
761 static const bool check_nb_all [FRAMED_NEIGHBOR_COUNT] =
762 {1,1,1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1};
763 const bool *check_nb = check_nb_all;
765 // neighbours checks for frames visibility
766 if (H_merge || V_merge) {
768 check_nb = check_nb_vertical; // vertical-only merge
770 check_nb = check_nb_horizontal; // horizontal-only merge
771 content_t current = n.getContent();
772 for (int i = 0; i < FRAMED_NEIGHBOR_COUNT; i++) {
775 v3s16 n2p = blockpos_nodes + p + g_26dirs[i];
776 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
777 content_t n2c = n2.getContent();
785 static const u8 nb_triplet[FRAMED_EDGE_COUNT][3] = {
786 {1, 2, 7}, {1, 5, 6}, {4, 2, 15}, {4, 5, 14},
787 {2, 0, 11}, {2, 3, 13}, {5, 0, 10}, {5, 3, 12},
788 {0, 1, 8}, {0, 4, 16}, {3, 4, 17}, {3, 1, 9},
792 for (int edge = 0; edge < FRAMED_EDGE_COUNT; edge++) {
794 if (nb[nb_triplet[edge][2]])
795 edge_invisible = nb[nb_triplet[edge][0]] & nb[nb_triplet[edge][1]];
797 edge_invisible = nb[nb_triplet[edge][0]] ^ nb[nb_triplet[edge][1]];
800 drawAutoLightedCuboid(frame_edges[edge]);
803 for (int face = 0; face < 6; face++) {
807 tile = glass_tiles[face];
816 for (v3f &vertex : vertices) {
819 vertex.rotateXZBy(180); break;
821 vertex.rotateYZBy( 90); break;
823 vertex.rotateXZBy( 90); break;
825 vertex.rotateXZBy( 0); break;
827 vertex.rotateYZBy(-90); break;
829 vertex.rotateXZBy(-90); break;
832 v3s16 dir = g_6dirs[face];
833 drawQuad(vertices, dir);
836 // Optionally render internal liquid level defined by param2
837 // Liquid is textured with 1 tile defined in nodedef 'special_tiles'
838 if (param2 > 0 && f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL &&
839 f->special_tiles[0].layers[0].texture) {
840 // Internal liquid level has param2 range 0 .. 63,
841 // convert it to -0.5 .. 0.5
842 float vlev = (param2 / 63.0f) * 2.0f - 1.0f;
843 getSpecialTile(0, &tile);
844 drawAutoLightedCuboid(aabb3f(-(nb[5] ? g : b),
848 (nb[1] ? g : b) * vlev,
853 void MapblockMeshGenerator::drawAllfacesNode()
855 static const aabb3f box(-BS / 2, -BS / 2, -BS / 2, BS / 2, BS / 2, BS / 2);
857 drawAutoLightedCuboid(box);
860 void MapblockMeshGenerator::drawTorchlikeNode()
862 u8 wall = n.getWallMounted(nodedef);
865 case DWM_YP: tileindex = 1; break; // ceiling
866 case DWM_YN: tileindex = 0; break; // floor
867 default: tileindex = 2; // side (or invalid—should we care?)
869 useTile(tileindex, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
871 float size = BS / 2 * f->visual_scale;
875 v3f( size, -size, 0),
876 v3f(-size, -size, 0),
879 for (v3f &vertex : vertices) {
882 vertex.Y += -size + BS/2;
883 vertex.rotateXZBy(-45);
886 vertex.Y += size - BS/2;
887 vertex.rotateXZBy(45);
890 vertex.X += -size + BS/2;
893 vertex.X += -size + BS/2;
894 vertex.rotateXZBy(180);
897 vertex.X += -size + BS/2;
898 vertex.rotateXZBy(90);
901 vertex.X += -size + BS/2;
902 vertex.rotateXZBy(-90);
908 void MapblockMeshGenerator::drawSignlikeNode()
910 u8 wall = n.getWallMounted(nodedef);
911 useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
912 static const float offset = BS / 16;
913 float size = BS / 2 * f->visual_scale;
914 // Wall at X+ of node
916 v3f(BS / 2 - offset, size, size),
917 v3f(BS / 2 - offset, size, -size),
918 v3f(BS / 2 - offset, -size, -size),
919 v3f(BS / 2 - offset, -size, size),
922 for (v3f &vertex : vertices) {
925 vertex.rotateXYBy( 90); break;
927 vertex.rotateXYBy(-90); break;
929 vertex.rotateXZBy( 0); break;
931 vertex.rotateXZBy(180); break;
933 vertex.rotateXZBy( 90); break;
935 vertex.rotateXZBy(-90); break;
941 void MapblockMeshGenerator::drawPlantlikeQuad(float rotation, float quad_offset,
942 bool offset_top_only)
945 v3f(-scale, -BS / 2 + 2.0 * scale * plant_height, 0),
946 v3f( scale, -BS / 2 + 2.0 * scale * plant_height, 0),
947 v3f( scale, -BS / 2, 0),
948 v3f(-scale, -BS / 2, 0),
950 if (random_offset_Y) {
951 PseudoRandom yrng(face_num++ | p.X << 16 | p.Z << 8 | p.Y << 24);
952 offset.Y = -BS * ((yrng.next() % 16 / 16.0) * 0.125);
954 int offset_count = offset_top_only ? 2 : 4;
955 for (int i = 0; i < offset_count; i++)
956 vertices[i].Z += quad_offset;
958 for (v3f &vertex : vertices) {
959 vertex.rotateXZBy(rotation + rotate_degree);
962 drawQuad(vertices, v3s16(0, 0, 0), plant_height);
965 void MapblockMeshGenerator::drawPlantlike()
967 draw_style = PLANT_STYLE_CROSS;
968 scale = BS / 2 * f->visual_scale;
969 offset = v3f(0, 0, 0);
971 random_offset_Y = false;
975 switch (f->param_type_2) {
976 case CPT2_MESHOPTIONS:
977 draw_style = PlantlikeStyle(n.param2 & MO_MASK_STYLE);
978 if (n.param2 & MO_BIT_SCALE_SQRT2)
980 if (n.param2 & MO_BIT_RANDOM_OFFSET) {
981 PseudoRandom rng(p.X << 8 | p.Z | p.Y << 16);
982 offset.X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
983 offset.Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
985 if (n.param2 & MO_BIT_RANDOM_OFFSET_Y)
986 random_offset_Y = true;
990 rotate_degree = n.param2 * 2;
994 plant_height = n.param2 / 16.0;
1001 switch (draw_style) {
1002 case PLANT_STYLE_CROSS:
1003 drawPlantlikeQuad(46);
1004 drawPlantlikeQuad(-44);
1007 case PLANT_STYLE_CROSS2:
1008 drawPlantlikeQuad(91);
1009 drawPlantlikeQuad(1);
1012 case PLANT_STYLE_STAR:
1013 drawPlantlikeQuad(121);
1014 drawPlantlikeQuad(241);
1015 drawPlantlikeQuad(1);
1018 case PLANT_STYLE_HASH:
1019 drawPlantlikeQuad( 1, BS / 4);
1020 drawPlantlikeQuad( 91, BS / 4);
1021 drawPlantlikeQuad(181, BS / 4);
1022 drawPlantlikeQuad(271, BS / 4);
1025 case PLANT_STYLE_HASH2:
1026 drawPlantlikeQuad( 1, -BS / 2, true);
1027 drawPlantlikeQuad( 91, -BS / 2, true);
1028 drawPlantlikeQuad(181, -BS / 2, true);
1029 drawPlantlikeQuad(271, -BS / 2, true);
1034 void MapblockMeshGenerator::drawPlantlikeNode()
1040 void MapblockMeshGenerator::drawPlantlikeRootedNode()
1042 useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, 0, true);
1043 origin += v3f(0.0, BS, 0.0);
1045 if (data->m_smooth_lighting) {
1046 getSmoothLightFrame();
1048 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1049 light = LightPair(getInteriorLight(ntop, 1, nodedef));
1055 void MapblockMeshGenerator::drawFirelikeQuad(float rotation, float opening_angle,
1056 float offset_h, float offset_v)
1059 v3f(-scale, -BS / 2 + scale * 2, 0),
1060 v3f( scale, -BS / 2 + scale * 2, 0),
1061 v3f( scale, -BS / 2, 0),
1062 v3f(-scale, -BS / 2, 0),
1065 for (v3f &vertex : vertices) {
1066 vertex.rotateYZBy(opening_angle);
1067 vertex.Z += offset_h;
1068 vertex.rotateXZBy(rotation);
1069 vertex.Y += offset_v;
1074 void MapblockMeshGenerator::drawFirelikeNode()
1077 scale = BS / 2 * f->visual_scale;
1079 // Check for adjacent nodes
1080 bool neighbors = false;
1081 bool neighbor[6] = {0, 0, 0, 0, 0, 0};
1082 content_t current = n.getContent();
1083 for (int i = 0; i < 6; i++) {
1084 v3s16 n2p = blockpos_nodes + p + g_6dirs[i];
1085 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
1086 content_t n2c = n2.getContent();
1087 if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
1092 bool drawBasicFire = neighbor[D6D_YN] || !neighbors;
1093 bool drawBottomFire = neighbor[D6D_YP];
1095 if (drawBasicFire || neighbor[D6D_ZP])
1096 drawFirelikeQuad(0, -10, 0.4 * BS);
1097 else if (drawBottomFire)
1098 drawFirelikeQuad(0, 70, 0.47 * BS, 0.484 * BS);
1100 if (drawBasicFire || neighbor[D6D_XN])
1101 drawFirelikeQuad(90, -10, 0.4 * BS);
1102 else if (drawBottomFire)
1103 drawFirelikeQuad(90, 70, 0.47 * BS, 0.484 * BS);
1105 if (drawBasicFire || neighbor[D6D_ZN])
1106 drawFirelikeQuad(180, -10, 0.4 * BS);
1107 else if (drawBottomFire)
1108 drawFirelikeQuad(180, 70, 0.47 * BS, 0.484 * BS);
1110 if (drawBasicFire || neighbor[D6D_XP])
1111 drawFirelikeQuad(270, -10, 0.4 * BS);
1112 else if (drawBottomFire)
1113 drawFirelikeQuad(270, 70, 0.47 * BS, 0.484 * BS);
1115 if (drawBasicFire) {
1116 drawFirelikeQuad(45, 0, 0.0);
1117 drawFirelikeQuad(-45, 0, 0.0);
1121 void MapblockMeshGenerator::drawFencelikeNode()
1124 TileSpec tile_nocrack = tile;
1126 for (auto &layer : tile_nocrack.layers)
1127 layer.material_flags &= ~MATERIAL_FLAG_CRACK;
1129 // Put wood the right way around in the posts
1130 TileSpec tile_rot = tile;
1131 tile_rot.rotation = 1;
1133 static const f32 post_rad = BS / 8;
1134 static const f32 bar_rad = BS / 16;
1135 static const f32 bar_len = BS / 2 - post_rad;
1137 // The post - always present
1138 static const aabb3f post(-post_rad, -BS / 2, -post_rad,
1139 post_rad, BS / 2, post_rad);
1140 static const f32 postuv[24] = {
1141 0.375, 0.375, 0.625, 0.625,
1142 0.375, 0.375, 0.625, 0.625,
1143 0.000, 0.000, 0.250, 1.000,
1144 0.250, 0.000, 0.500, 1.000,
1145 0.500, 0.000, 0.750, 1.000,
1146 0.750, 0.000, 1.000, 1.000,
1149 drawAutoLightedCuboid(post, postuv);
1151 tile = tile_nocrack;
1153 // Now a section of fence, +X, if there's a post there
1156 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1157 const ContentFeatures *f2 = &nodedef->get(n2);
1158 if (f2->drawtype == NDT_FENCELIKE) {
1159 static const aabb3f bar_x1(BS / 2 - bar_len, BS / 4 - bar_rad, -bar_rad,
1160 BS / 2 + bar_len, BS / 4 + bar_rad, bar_rad);
1161 static const aabb3f bar_x2(BS / 2 - bar_len, -BS / 4 - bar_rad, -bar_rad,
1162 BS / 2 + bar_len, -BS / 4 + bar_rad, bar_rad);
1163 static const f32 xrailuv[24] = {
1164 0.000, 0.125, 1.000, 0.250,
1165 0.000, 0.250, 1.000, 0.375,
1166 0.375, 0.375, 0.500, 0.500,
1167 0.625, 0.625, 0.750, 0.750,
1168 0.000, 0.500, 1.000, 0.625,
1169 0.000, 0.875, 1.000, 1.000,
1171 drawAutoLightedCuboid(bar_x1, xrailuv);
1172 drawAutoLightedCuboid(bar_x2, xrailuv);
1175 // Now a section of fence, +Z, if there's a post there
1178 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1179 f2 = &nodedef->get(n2);
1180 if (f2->drawtype == NDT_FENCELIKE) {
1181 static const aabb3f bar_z1(-bar_rad, BS / 4 - bar_rad, BS / 2 - bar_len,
1182 bar_rad, BS / 4 + bar_rad, BS / 2 + bar_len);
1183 static const aabb3f bar_z2(-bar_rad, -BS / 4 - bar_rad, BS / 2 - bar_len,
1184 bar_rad, -BS / 4 + bar_rad, BS / 2 + bar_len);
1185 static const f32 zrailuv[24] = {
1186 0.1875, 0.0625, 0.3125, 0.3125, // cannot rotate; stretch
1187 0.2500, 0.0625, 0.3750, 0.3125, // for wood texture instead
1188 0.0000, 0.5625, 1.0000, 0.6875,
1189 0.0000, 0.3750, 1.0000, 0.5000,
1190 0.3750, 0.3750, 0.5000, 0.5000,
1191 0.6250, 0.6250, 0.7500, 0.7500,
1193 drawAutoLightedCuboid(bar_z1, zrailuv);
1194 drawAutoLightedCuboid(bar_z2, zrailuv);
1198 bool MapblockMeshGenerator::isSameRail(v3s16 dir)
1200 MapNode node2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
1201 if (node2.getContent() == n.getContent())
1203 const ContentFeatures &def2 = nodedef->get(node2);
1204 return ((def2.drawtype == NDT_RAILLIKE) &&
1205 (def2.getGroup(raillike_groupname) == raillike_group));
1209 static const v3s16 rail_direction[4] = {
1215 static const int rail_slope_angle[4] = {0, 180, 90, -90};
1227 static const RailDesc rail_kinds[16] = {
1230 {straight, 0}, // . . . .
1231 {straight, 0}, // . . . +Z
1232 {straight, 0}, // . . -Z .
1233 {straight, 0}, // . . -Z +Z
1234 {straight, 90}, // . -X . .
1235 { curved, 180}, // . -X . +Z
1236 { curved, 270}, // . -X -Z .
1237 {junction, 180}, // . -X -Z +Z
1238 {straight, 90}, // +X . . .
1239 { curved, 90}, // +X . . +Z
1240 { curved, 0}, // +X . -Z .
1241 {junction, 0}, // +X . -Z +Z
1242 {straight, 90}, // +X -X . .
1243 {junction, 90}, // +X -X . +Z
1244 {junction, 270}, // +X -X -Z .
1245 { cross, 0}, // +X -X -Z +Z
1249 void MapblockMeshGenerator::drawRaillikeNode()
1251 raillike_group = nodedef->get(n).getGroup(raillike_groupname);
1256 bool sloped = false;
1257 for (int dir = 0; dir < 4; dir++) {
1258 bool rail_above = isSameRail(rail_direction[dir] + v3s16(0, 1, 0));
1261 angle = rail_slope_angle[dir];
1264 isSameRail(rail_direction[dir]) ||
1265 isSameRail(rail_direction[dir] + v3s16(0, -1, 0)))
1270 tile_index = straight;
1272 tile_index = rail_kinds[code].tile_index;
1273 angle = rail_kinds[code].angle;
1276 useTile(tile_index, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
1278 static const float offset = BS / 64;
1279 static const float size = BS / 2;
1280 float y2 = sloped ? size : -size;
1282 v3f(-size, y2 + offset, size),
1283 v3f( size, y2 + offset, size),
1284 v3f( size, -size + offset, -size),
1285 v3f(-size, -size + offset, -size),
1288 for (v3f &vertex : vertices)
1289 vertex.rotateXZBy(angle);
1294 static const v3s16 nodebox_tile_dirs[6] = {
1303 // we have this order for some reason...
1304 static const v3s16 nodebox_connection_dirs[6] = {
1305 v3s16( 0, 1, 0), // top
1306 v3s16( 0, -1, 0), // bottom
1307 v3s16( 0, 0, -1), // front
1308 v3s16(-1, 0, 0), // left
1309 v3s16( 0, 0, 1), // back
1310 v3s16( 1, 0, 0), // right
1314 void MapblockMeshGenerator::drawNodeboxNode()
1317 for (int face = 0; face < 6; face++) {
1318 // Handles facedir rotation for textures
1319 getTile(nodebox_tile_dirs[face], &tiles[face]);
1322 // locate possible neighboring nodes to connect to
1323 u8 neighbors_set = 0;
1324 if (f->node_box.type == NODEBOX_CONNECTED) {
1325 for (int dir = 0; dir != 6; dir++) {
1327 v3s16 p2 = blockpos_nodes + p + nodebox_connection_dirs[dir];
1328 MapNode n2 = data->m_vmanip.getNodeNoEx(p2);
1329 if (nodedef->nodeboxConnects(n, n2, flag))
1330 neighbors_set |= flag;
1334 std::vector<aabb3f> boxes;
1335 n.getNodeBoxes(nodedef, &boxes, neighbors_set);
1336 for (auto &box : boxes)
1337 drawAutoLightedCuboid(box, nullptr, tiles, 6);
1340 void MapblockMeshGenerator::drawMeshNode()
1344 bool private_mesh; // as a grab/drop pair is not thread-safe
1346 if (f->param_type_2 == CPT2_FACEDIR ||
1347 f->param_type_2 == CPT2_COLORED_FACEDIR) {
1348 facedir = n.getFaceDir(nodedef);
1349 } else if (f->param_type_2 == CPT2_WALLMOUNTED ||
1350 f->param_type_2 == CPT2_COLORED_WALLMOUNTED) {
1351 // Convert wallmounted to 6dfacedir.
1352 // When cache enabled, it is already converted.
1353 facedir = n.getWallMounted(nodedef);
1354 if (!enable_mesh_cache)
1355 facedir = wallmounted_to_facedir[facedir];
1358 if (!data->m_smooth_lighting && f->mesh_ptr[facedir]) {
1359 // use cached meshes
1360 private_mesh = false;
1361 mesh = f->mesh_ptr[facedir];
1362 } else if (f->mesh_ptr[0]) {
1363 // no cache, clone and rotate mesh
1364 private_mesh = true;
1365 mesh = cloneMesh(f->mesh_ptr[0]);
1366 rotateMeshBy6dFacedir(mesh, facedir);
1367 recalculateBoundingBox(mesh);
1368 meshmanip->recalculateNormals(mesh, true, false);
1372 int mesh_buffer_count = mesh->getMeshBufferCount();
1373 for (int j = 0; j < mesh_buffer_count; j++) {
1375 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1376 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1377 int vertex_count = buf->getVertexCount();
1379 if (data->m_smooth_lighting) {
1380 // Mesh is always private here. So the lighting is applied to each
1381 // vertex right here.
1382 for (int k = 0; k < vertex_count; k++) {
1383 video::S3DVertex &vertex = vertices[k];
1384 vertex.Color = blendLightColor(vertex.Pos, vertex.Normal);
1385 vertex.Pos += origin;
1387 collector->append(tile, vertices, vertex_count,
1388 buf->getIndices(), buf->getIndexCount());
1390 // Don't modify the mesh, it may not be private here.
1391 // Instead, let the collector process colors, etc.
1392 collector->append(tile, vertices, vertex_count,
1393 buf->getIndices(), buf->getIndexCount(), origin,
1394 color, f->light_source);
1401 // also called when the drawtype is known but should have been pre-converted
1402 void MapblockMeshGenerator::errorUnknownDrawtype()
1404 infostream << "Got drawtype " << f->drawtype << std::endl;
1405 FATAL_ERROR("Unknown drawtype");
1408 void MapblockMeshGenerator::drawNode()
1410 // skip some drawtypes early
1411 switch (f->drawtype) {
1412 case NDT_NORMAL: // Drawn by MapBlockMesh
1413 case NDT_AIRLIKE: // Not drawn at all
1414 case NDT_LIQUID: // Drawn by MapBlockMesh
1419 origin = intToFloat(p, BS);
1420 if (data->m_smooth_lighting)
1421 getSmoothLightFrame();
1423 light = LightPair(getInteriorLight(n, 1, nodedef));
1424 switch (f->drawtype) {
1425 case NDT_FLOWINGLIQUID: drawLiquidNode(); break;
1426 case NDT_GLASSLIKE: drawGlasslikeNode(); break;
1427 case NDT_GLASSLIKE_FRAMED: drawGlasslikeFramedNode(); break;
1428 case NDT_ALLFACES: drawAllfacesNode(); break;
1429 case NDT_TORCHLIKE: drawTorchlikeNode(); break;
1430 case NDT_SIGNLIKE: drawSignlikeNode(); break;
1431 case NDT_PLANTLIKE: drawPlantlikeNode(); break;
1432 case NDT_PLANTLIKE_ROOTED: drawPlantlikeRootedNode(); break;
1433 case NDT_FIRELIKE: drawFirelikeNode(); break;
1434 case NDT_FENCELIKE: drawFencelikeNode(); break;
1435 case NDT_RAILLIKE: drawRaillikeNode(); break;
1436 case NDT_NODEBOX: drawNodeboxNode(); break;
1437 case NDT_MESH: drawMeshNode(); break;
1438 default: errorUnknownDrawtype(); break;
1443 TODO: Fix alpha blending for special nodes
1444 Currently only the last element rendered is blended correct
1446 void MapblockMeshGenerator::generate()
1448 for (p.Z = 0; p.Z < MAP_BLOCKSIZE; p.Z++)
1449 for (p.Y = 0; p.Y < MAP_BLOCKSIZE; p.Y++)
1450 for (p.X = 0; p.X < MAP_BLOCKSIZE; p.X++) {
1451 n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1452 f = &nodedef->get(n);
1457 void MapblockMeshGenerator::renderSingle(content_t node)
1460 n = MapNode(node, 0xff, 0x00);
1461 f = &nodedef->get(n);