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/meshgen/collector.h"
30 #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 getTile(index, &tile);
82 if (!data->m_smooth_lighting)
83 color = encode_light(light, f->light_source);
85 for (auto &layer : tile.layers) {
86 layer.material_flags |= set_flags;
87 layer.material_flags &= ~reset_flags;
91 // Returns a tile, ready for use, non-rotated.
92 void MapblockMeshGenerator::getTile(int index, TileSpec *tile)
94 getNodeTileN(n, p, index, data, *tile);
97 // Returns a tile, ready for use, rotated according to the node facedir.
98 void MapblockMeshGenerator::getTile(v3s16 direction, TileSpec *tile)
100 getNodeTile(n, p, direction, data, *tile);
103 // Returns a special tile, ready for use, non-rotated.
104 void MapblockMeshGenerator::getSpecialTile(int index, TileSpec *tile, bool apply_crack)
106 *tile = f->special_tiles[index];
107 TileLayer *top_layer = nullptr;
109 for (auto &layernum : tile->layers) {
110 TileLayer *layer = &layernum;
111 if (layer->texture_id == 0)
114 if (!layer->has_color)
115 n.getColor(*f, &layer->color);
119 top_layer->material_flags |= MATERIAL_FLAG_CRACK;
122 void MapblockMeshGenerator::drawQuad(v3f *coords, const v3s16 &normal,
123 float vertical_tiling)
125 const v2f tcoords[4] = {v2f(0.0, 0.0), v2f(1.0, 0.0),
126 v2f(1.0, vertical_tiling), v2f(0.0, vertical_tiling)};
127 video::S3DVertex vertices[4];
128 bool shade_face = !f->light_source && (normal != v3s16(0, 0, 0));
129 v3f normal2(normal.X, normal.Y, normal.Z);
130 for (int j = 0; j < 4; j++) {
131 vertices[j].Pos = coords[j] + origin;
132 vertices[j].Normal = normal2;
133 if (data->m_smooth_lighting)
134 vertices[j].Color = blendLightColor(coords[j]);
136 vertices[j].Color = color;
138 applyFacesShading(vertices[j].Color, normal2);
139 vertices[j].TCoords = tcoords[j];
141 collector->append(tile, vertices, 4, quad_indices, 6);
145 // tiles - the tiles (materials) to use (for all 6 faces)
146 // tilecount - number of entries in tiles, 1<=tilecount<=6
147 // lights - vertex light levels. The order is the same as in light_dirs.
148 // NULL may be passed if smooth lighting is disabled.
149 // txc - texture coordinates - this is a list of texture coordinates
150 // for the opposite corners of each face - therefore, there
151 // should be (2+2)*6=24 values in the list. The order of
152 // the faces in the list is up-down-right-left-back-front
153 // (compatible with ContentFeatures).
154 void MapblockMeshGenerator::drawCuboid(const aabb3f &box,
155 TileSpec *tiles, int tilecount, const LightInfo *lights, const f32 *txc)
157 assert(tilecount >= 1 && tilecount <= 6); // pre-condition
159 v3f min = box.MinEdge;
160 v3f max = box.MaxEdge;
162 video::SColor colors[6];
163 if (!data->m_smooth_lighting) {
164 for (int face = 0; face != 6; ++face) {
165 colors[face] = encode_light(light, f->light_source);
167 if (!f->light_source) {
168 applyFacesShading(colors[0], v3f(0, 1, 0));
169 applyFacesShading(colors[1], v3f(0, -1, 0));
170 applyFacesShading(colors[2], v3f(1, 0, 0));
171 applyFacesShading(colors[3], v3f(-1, 0, 0));
172 applyFacesShading(colors[4], v3f(0, 0, 1));
173 applyFacesShading(colors[5], v3f(0, 0, -1));
177 video::S3DVertex vertices[24] = {
179 video::S3DVertex(min.X, max.Y, max.Z, 0, 1, 0, colors[0], txc[0], txc[1]),
180 video::S3DVertex(max.X, max.Y, max.Z, 0, 1, 0, colors[0], txc[2], txc[1]),
181 video::S3DVertex(max.X, max.Y, min.Z, 0, 1, 0, colors[0], txc[2], txc[3]),
182 video::S3DVertex(min.X, max.Y, min.Z, 0, 1, 0, colors[0], txc[0], txc[3]),
184 video::S3DVertex(min.X, min.Y, min.Z, 0, -1, 0, colors[1], txc[4], txc[5]),
185 video::S3DVertex(max.X, min.Y, min.Z, 0, -1, 0, colors[1], txc[6], txc[5]),
186 video::S3DVertex(max.X, min.Y, max.Z, 0, -1, 0, colors[1], txc[6], txc[7]),
187 video::S3DVertex(min.X, min.Y, max.Z, 0, -1, 0, colors[1], txc[4], txc[7]),
189 video::S3DVertex(max.X, max.Y, min.Z, 1, 0, 0, colors[2], txc[ 8], txc[9]),
190 video::S3DVertex(max.X, max.Y, max.Z, 1, 0, 0, colors[2], txc[10], txc[9]),
191 video::S3DVertex(max.X, min.Y, max.Z, 1, 0, 0, colors[2], txc[10], txc[11]),
192 video::S3DVertex(max.X, min.Y, min.Z, 1, 0, 0, colors[2], txc[ 8], txc[11]),
194 video::S3DVertex(min.X, max.Y, max.Z, -1, 0, 0, colors[3], txc[12], txc[13]),
195 video::S3DVertex(min.X, max.Y, min.Z, -1, 0, 0, colors[3], txc[14], txc[13]),
196 video::S3DVertex(min.X, min.Y, min.Z, -1, 0, 0, colors[3], txc[14], txc[15]),
197 video::S3DVertex(min.X, min.Y, max.Z, -1, 0, 0, colors[3], txc[12], txc[15]),
199 video::S3DVertex(max.X, max.Y, max.Z, 0, 0, 1, colors[4], txc[16], txc[17]),
200 video::S3DVertex(min.X, max.Y, max.Z, 0, 0, 1, colors[4], txc[18], txc[17]),
201 video::S3DVertex(min.X, min.Y, max.Z, 0, 0, 1, colors[4], txc[18], txc[19]),
202 video::S3DVertex(max.X, min.Y, max.Z, 0, 0, 1, colors[4], txc[16], txc[19]),
204 video::S3DVertex(min.X, max.Y, min.Z, 0, 0, -1, colors[5], txc[20], txc[21]),
205 video::S3DVertex(max.X, max.Y, min.Z, 0, 0, -1, colors[5], txc[22], txc[21]),
206 video::S3DVertex(max.X, min.Y, min.Z, 0, 0, -1, colors[5], txc[22], txc[23]),
207 video::S3DVertex(min.X, min.Y, min.Z, 0, 0, -1, colors[5], txc[20], txc[23]),
210 static const u8 light_indices[24] = {
219 for (int face = 0; face < 6; face++) {
220 int tileindex = MYMIN(face, tilecount - 1);
221 const TileSpec &tile = tiles[tileindex];
222 for (int j = 0; j < 4; j++) {
223 video::S3DVertex &vertex = vertices[face * 4 + j];
224 v2f &tcoords = vertex.TCoords;
225 switch (tile.rotation) {
229 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
232 tcoords.rotateBy(180, irr::core::vector2df(0, 0));
235 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
238 tcoords.X = 1.0 - tcoords.X;
239 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
242 tcoords.X = 1.0 - tcoords.X;
243 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
246 tcoords.Y = 1.0 - tcoords.Y;
247 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
250 tcoords.Y = 1.0 - tcoords.Y;
251 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
254 tcoords.X = 1.0 - tcoords.X;
257 tcoords.Y = 1.0 - tcoords.Y;
265 if (data->m_smooth_lighting) {
266 for (int j = 0; j < 24; ++j) {
267 video::S3DVertex &vertex = vertices[j];
268 vertex.Color = encode_light(
269 lights[light_indices[j]].getPair(MYMAX(0.0f, vertex.Normal.Y)),
271 if (!f->light_source)
272 applyFacesShading(vertex.Color, vertex.Normal);
276 // Add to mesh collector
277 for (int k = 0; k < 6; ++k) {
278 int tileindex = MYMIN(k, tilecount - 1);
279 collector->append(tiles[tileindex], vertices + 4 * k, 4, quad_indices, 6);
283 // Gets the base lighting values for a node
284 void MapblockMeshGenerator::getSmoothLightFrame()
286 for (int k = 0; k < 8; ++k)
287 frame.sunlight[k] = false;
288 for (int k = 0; k < 8; ++k) {
289 LightPair light(getSmoothLightTransparent(blockpos_nodes + p, light_dirs[k], data));
290 frame.lightsDay[k] = light.lightDay;
291 frame.lightsNight[k] = light.lightNight;
292 // If there is direct sunlight and no ambient occlusion at some corner,
293 // mark the vertical edge (top and bottom corners) containing it.
294 if (light.lightDay == 255) {
295 frame.sunlight[k] = true;
296 frame.sunlight[k ^ 2] = true;
301 // Calculates vertex light level
302 // vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
303 LightInfo MapblockMeshGenerator::blendLight(const v3f &vertex_pos)
305 // Light levels at (logical) node corners are known. Here,
306 // trilinear interpolation is used to calculate light level
307 // at a given point in the node.
308 f32 x = core::clamp(vertex_pos.X / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
309 f32 y = core::clamp(vertex_pos.Y / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
310 f32 z = core::clamp(vertex_pos.Z / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
311 f32 lightDay = 0.0; // daylight
312 f32 lightNight = 0.0;
313 f32 lightBoosted = 0.0; // daylight + direct sunlight, if any
314 for (int k = 0; k < 8; ++k) {
315 f32 dx = (k & 4) ? x : 1 - x;
316 f32 dy = (k & 2) ? y : 1 - y;
317 f32 dz = (k & 1) ? z : 1 - z;
318 // Use direct sunlight (255), if any; use daylight otherwise.
319 f32 light_boosted = frame.sunlight[k] ? 255 : frame.lightsDay[k];
320 lightDay += dx * dy * dz * frame.lightsDay[k];
321 lightNight += dx * dy * dz * frame.lightsNight[k];
322 lightBoosted += dx * dy * dz * light_boosted;
324 return LightInfo{lightDay, lightNight, lightBoosted};
327 // Calculates vertex color to be used in mapblock mesh
328 // vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
329 // tile_color - node's tile color
330 video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos)
332 LightInfo light = blendLight(vertex_pos);
333 return encode_light(light.getPair(), f->light_source);
336 video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos,
337 const v3f &vertex_normal)
339 LightInfo light = blendLight(vertex_pos);
340 video::SColor color = encode_light(light.getPair(MYMAX(0.0f, vertex_normal.Y)), f->light_source);
341 if (!f->light_source)
342 applyFacesShading(color, vertex_normal);
346 void MapblockMeshGenerator::generateCuboidTextureCoords(const aabb3f &box, f32 *coords)
348 f32 tx1 = (box.MinEdge.X / BS) + 0.5;
349 f32 ty1 = (box.MinEdge.Y / BS) + 0.5;
350 f32 tz1 = (box.MinEdge.Z / BS) + 0.5;
351 f32 tx2 = (box.MaxEdge.X / BS) + 0.5;
352 f32 ty2 = (box.MaxEdge.Y / BS) + 0.5;
353 f32 tz2 = (box.MaxEdge.Z / BS) + 0.5;
355 tx1, 1 - tz2, tx2, 1 - tz1, // up
356 tx1, tz1, tx2, tz2, // down
357 tz1, 1 - ty2, tz2, 1 - ty1, // right
358 1 - tz2, 1 - ty2, 1 - tz1, 1 - ty1, // left
359 1 - tx2, 1 - ty2, 1 - tx1, 1 - ty1, // back
360 tx1, 1 - ty2, tx2, 1 - ty1, // front
362 for (int i = 0; i != 24; ++i)
366 void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc,
367 TileSpec *tiles, int tile_count)
369 f32 texture_coord_buf[24];
370 f32 dx1 = box.MinEdge.X;
371 f32 dy1 = box.MinEdge.Y;
372 f32 dz1 = box.MinEdge.Z;
373 f32 dx2 = box.MaxEdge.X;
374 f32 dy2 = box.MaxEdge.Y;
375 f32 dz2 = box.MaxEdge.Z;
376 box.MinEdge += origin;
377 box.MaxEdge += origin;
379 generateCuboidTextureCoords(box, texture_coord_buf);
380 txc = texture_coord_buf;
386 if (data->m_smooth_lighting) {
388 for (int j = 0; j < 8; ++j) {
390 d.X = (j & 4) ? dx2 : dx1;
391 d.Y = (j & 2) ? dy2 : dy1;
392 d.Z = (j & 1) ? dz2 : dz1;
393 lights[j] = blendLight(d);
395 drawCuboid(box, tiles, tile_count, lights, txc);
397 drawCuboid(box, tiles, tile_count, nullptr, txc);
401 void MapblockMeshGenerator::prepareLiquidNodeDrawing()
403 getSpecialTile(0, &tile_liquid_top);
404 getSpecialTile(1, &tile_liquid);
406 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y + 1, p.Z));
407 MapNode nbottom = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y - 1, p.Z));
408 c_flowing = nodedef->getId(f->liquid_alternative_flowing);
409 c_source = nodedef->getId(f->liquid_alternative_source);
410 top_is_same_liquid = (ntop.getContent() == c_flowing) || (ntop.getContent() == c_source);
411 draw_liquid_bottom = (nbottom.getContent() != c_flowing) && (nbottom.getContent() != c_source);
412 if (draw_liquid_bottom) {
413 const ContentFeatures &f2 = nodedef->get(nbottom.getContent());
414 if (f2.solidness > 1)
415 draw_liquid_bottom = false;
418 if (data->m_smooth_lighting)
419 return; // don't need to pre-compute anything in this case
421 if (f->light_source != 0) {
422 // If this liquid emits light and doesn't contain light, draw
423 // it at what it emits, for an increased effect
424 u8 e = decode_light(f->light_source);
425 light = LightPair(std::max(e, light.lightDay), std::max(e, light.lightNight));
426 } else if (nodedef->get(ntop).param_type == CPT_LIGHT) {
427 // Otherwise, use the light of the node on top if possible
428 light = LightPair(getInteriorLight(ntop, 0, nodedef));
431 color_liquid_top = encode_light(light, f->light_source);
432 color = encode_light(light, f->light_source);
435 void MapblockMeshGenerator::getLiquidNeighborhood()
437 u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8);
439 for (int w = -1; w <= 1; w++)
440 for (int u = -1; u <= 1; u++) {
441 NeighborData &neighbor = liquid_neighbors[w + 1][u + 1];
442 v3s16 p2 = p + v3s16(u, 0, w);
443 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
444 neighbor.content = n2.getContent();
445 neighbor.level = -0.5 * BS;
446 neighbor.is_same_liquid = false;
447 neighbor.top_is_same_liquid = false;
449 if (neighbor.content == CONTENT_IGNORE)
452 if (neighbor.content == c_source) {
453 neighbor.is_same_liquid = true;
454 neighbor.level = 0.5 * BS;
455 } else if (neighbor.content == c_flowing) {
456 neighbor.is_same_liquid = true;
457 u8 liquid_level = (n2.param2 & LIQUID_LEVEL_MASK);
458 if (liquid_level <= LIQUID_LEVEL_MAX + 1 - range)
461 liquid_level -= (LIQUID_LEVEL_MAX + 1 - range);
462 neighbor.level = (-0.5 + (liquid_level + 0.5) / range) * BS;
465 // Check node above neighbor.
466 // NOTE: This doesn't get executed if neighbor
469 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
470 if (n2.getContent() == c_source || n2.getContent() == c_flowing)
471 neighbor.top_is_same_liquid = true;
475 void MapblockMeshGenerator::calculateCornerLevels()
477 for (int k = 0; k < 2; k++)
478 for (int i = 0; i < 2; i++)
479 corner_levels[k][i] = getCornerLevel(i, k);
482 f32 MapblockMeshGenerator::getCornerLevel(int i, int k)
487 for (int dk = 0; dk < 2; dk++)
488 for (int di = 0; di < 2; di++) {
489 NeighborData &neighbor_data = liquid_neighbors[k + dk][i + di];
490 content_t content = neighbor_data.content;
492 // If top is liquid, draw starting from top of node
493 if (neighbor_data.top_is_same_liquid)
496 // Source always has the full height
497 if (content == c_source)
500 // Flowing liquid has level information
501 if (content == c_flowing) {
502 sum += neighbor_data.level;
504 } else if (content == CONTENT_AIR) {
507 return -0.5 * BS + 0.2;
515 void MapblockMeshGenerator::drawLiquidSides()
517 struct LiquidFaceDesc {
519 v3s16 p[2]; // XZ only; 1 means +, 0 means -
524 static const LiquidFaceDesc base_faces[4] = {
525 {v3s16( 1, 0, 0), {v3s16(1, 0, 1), v3s16(1, 0, 0)}},
526 {v3s16(-1, 0, 0), {v3s16(0, 0, 0), v3s16(0, 0, 1)}},
527 {v3s16( 0, 0, 1), {v3s16(0, 0, 1), v3s16(1, 0, 1)}},
528 {v3s16( 0, 0, -1), {v3s16(1, 0, 0), v3s16(0, 0, 0)}},
530 static const UV base_vertices[4] = {
537 for (const auto &face : base_faces) {
538 const NeighborData &neighbor = liquid_neighbors[face.dir.Z + 1][face.dir.X + 1];
540 // No face between nodes of the same liquid, unless there is node
541 // at the top to which it should be connected. Again, unless the face
542 // there would be inside the liquid
543 if (neighbor.is_same_liquid) {
544 if (!top_is_same_liquid)
546 if (neighbor.top_is_same_liquid)
550 const ContentFeatures &neighbor_features = nodedef->get(neighbor.content);
551 // Don't draw face if neighbor is blocking the view
552 if (neighbor_features.solidness == 2)
555 video::S3DVertex vertices[4];
556 for (int j = 0; j < 4; j++) {
557 const UV &vertex = base_vertices[j];
558 const v3s16 &base = face.p[vertex.u];
560 pos.X = (base.X - 0.5) * BS;
561 pos.Z = (base.Z - 0.5) * BS;
563 pos.Y = neighbor.is_same_liquid ? corner_levels[base.Z][base.X] : -0.5 * BS;
565 pos.Y = !top_is_same_liquid ? corner_levels[base.Z][base.X] : 0.5 * BS;
566 if (data->m_smooth_lighting)
567 color = blendLightColor(pos);
569 vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, color, vertex.u, vertex.v);
571 collector->append(tile_liquid, vertices, 4, quad_indices, 6);
575 void MapblockMeshGenerator::drawLiquidTop()
577 // To get backface culling right, the vertices need to go
578 // clockwise around the front of the face. And we happened to
579 // calculate corner levels in exact reverse order.
580 static const int corner_resolve[4][2] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
582 video::S3DVertex vertices[4] = {
583 video::S3DVertex(-BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 0, 1),
584 video::S3DVertex( BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 1, 1),
585 video::S3DVertex( BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 1, 0),
586 video::S3DVertex(-BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 0, 0),
589 for (int i = 0; i < 4; i++) {
590 int u = corner_resolve[i][0];
591 int w = corner_resolve[i][1];
592 vertices[i].Pos.Y += corner_levels[w][u];
593 if (data->m_smooth_lighting)
594 vertices[i].Color = blendLightColor(vertices[i].Pos);
595 vertices[i].Pos += origin;
598 // Default downwards-flowing texture animation goes from
599 // -Z towards +Z, thus the direction is +Z.
600 // Rotate texture to make animation go in flow direction
601 // Positive if liquid moves towards +Z
602 f32 dz = (corner_levels[0][0] + corner_levels[0][1]) -
603 (corner_levels[1][0] + corner_levels[1][1]);
604 // Positive if liquid moves towards +X
605 f32 dx = (corner_levels[0][0] + corner_levels[1][0]) -
606 (corner_levels[0][1] + corner_levels[1][1]);
607 f32 tcoord_angle = atan2(dz, dx) * core::RADTODEG;
608 v2f tcoord_center(0.5, 0.5);
609 v2f tcoord_translate(blockpos_nodes.Z + p.Z, blockpos_nodes.X + p.X);
610 tcoord_translate.rotateBy(tcoord_angle);
611 tcoord_translate.X -= floor(tcoord_translate.X);
612 tcoord_translate.Y -= floor(tcoord_translate.Y);
614 for (video::S3DVertex &vertex : vertices) {
615 vertex.TCoords.rotateBy(tcoord_angle, tcoord_center);
616 vertex.TCoords += tcoord_translate;
619 std::swap(vertices[0].TCoords, vertices[2].TCoords);
621 collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
624 void MapblockMeshGenerator::drawLiquidBottom()
626 video::S3DVertex vertices[4] = {
627 video::S3DVertex(-BS / 2, -BS / 2, -BS / 2, 0, 0, 0, color_liquid_top, 0, 0),
628 video::S3DVertex( BS / 2, -BS / 2, -BS / 2, 0, 0, 0, color_liquid_top, 1, 0),
629 video::S3DVertex( BS / 2, -BS / 2, BS / 2, 0, 0, 0, color_liquid_top, 1, 1),
630 video::S3DVertex(-BS / 2, -BS / 2, BS / 2, 0, 0, 0, color_liquid_top, 0, 1),
633 for (int i = 0; i < 4; i++) {
634 if (data->m_smooth_lighting)
635 vertices[i].Color = blendLightColor(vertices[i].Pos);
636 vertices[i].Pos += origin;
639 collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
642 void MapblockMeshGenerator::drawLiquidNode()
644 prepareLiquidNodeDrawing();
645 getLiquidNeighborhood();
646 calculateCornerLevels();
648 if (!top_is_same_liquid)
650 if (draw_liquid_bottom)
654 void MapblockMeshGenerator::drawGlasslikeNode()
658 for (int face = 0; face < 6; face++) {
659 // Check this neighbor
660 v3s16 dir = g_6dirs[face];
661 v3s16 neighbor_pos = blockpos_nodes + p + dir;
662 MapNode neighbor = data->m_vmanip.getNodeNoExNoEmerge(neighbor_pos);
663 // Don't make face if neighbor is of same type
664 if (neighbor.getContent() == n.getContent())
668 v3f(-BS / 2, BS / 2, -BS / 2),
669 v3f( BS / 2, BS / 2, -BS / 2),
670 v3f( BS / 2, -BS / 2, -BS / 2),
671 v3f(-BS / 2, -BS / 2, -BS / 2),
674 for (v3f &vertex : vertices) {
677 vertex.rotateXZBy(180); break;
679 vertex.rotateYZBy( 90); break;
681 vertex.rotateXZBy( 90); break;
683 vertex.rotateXZBy( 0); break;
685 vertex.rotateYZBy(-90); break;
687 vertex.rotateXZBy(-90); break;
690 drawQuad(vertices, dir);
694 void MapblockMeshGenerator::drawGlasslikeFramedNode()
697 for (int face = 0; face < 6; face++)
698 getTile(g_6dirs[face], &tiles[face]);
700 if (!data->m_smooth_lighting)
701 color = encode_light(light, f->light_source);
703 TileSpec glass_tiles[6];
704 for (auto &glass_tile : glass_tiles)
705 glass_tile = tiles[4];
707 u8 param2 = n.getParam2();
708 bool H_merge = !(param2 & 128);
709 bool V_merge = !(param2 & 64);
712 static const float a = BS / 2.0f;
713 static const float g = a - 0.03f;
714 static const float b = 0.876f * (BS / 2.0f);
716 static const aabb3f frame_edges[FRAMED_EDGE_COUNT] = {
717 aabb3f( b, b, -a, a, a, a), // y+
718 aabb3f(-a, b, -a, -b, a, a), // y+
719 aabb3f( b, -a, -a, a, -b, a), // y-
720 aabb3f(-a, -a, -a, -b, -b, a), // y-
721 aabb3f( b, -a, b, a, a, a), // x+
722 aabb3f( b, -a, -a, a, a, -b), // x+
723 aabb3f(-a, -a, b, -b, a, a), // x-
724 aabb3f(-a, -a, -a, -b, a, -b), // x-
725 aabb3f(-a, b, b, a, a, a), // z+
726 aabb3f(-a, -a, b, a, -b, a), // z+
727 aabb3f(-a, -a, -a, a, -b, -b), // z-
728 aabb3f(-a, b, -a, a, a, -b), // z-
731 // tables of neighbour (connect if same type and merge allowed),
732 // checked with g_26dirs
734 // 1 = connect, 0 = face visible
735 bool nb[FRAMED_NEIGHBOR_COUNT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
738 static const bool check_nb_vertical [FRAMED_NEIGHBOR_COUNT] =
739 {0,1,0,0,1,0, 0,0,0,0, 0,0,0,0, 0,0,0,0};
740 static const bool check_nb_horizontal [FRAMED_NEIGHBOR_COUNT] =
741 {1,0,1,1,0,1, 0,0,0,0, 1,1,1,1, 0,0,0,0};
742 static const bool check_nb_all [FRAMED_NEIGHBOR_COUNT] =
743 {1,1,1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1};
744 const bool *check_nb = check_nb_all;
746 // neighbours checks for frames visibility
747 if (H_merge || V_merge) {
749 check_nb = check_nb_vertical; // vertical-only merge
751 check_nb = check_nb_horizontal; // horizontal-only merge
752 content_t current = n.getContent();
753 for (int i = 0; i < FRAMED_NEIGHBOR_COUNT; i++) {
756 v3s16 n2p = blockpos_nodes + p + g_26dirs[i];
757 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
758 content_t n2c = n2.getContent();
766 static const u8 nb_triplet[FRAMED_EDGE_COUNT][3] = {
767 {1, 2, 7}, {1, 5, 6}, {4, 2, 15}, {4, 5, 14},
768 {2, 0, 11}, {2, 3, 13}, {5, 0, 10}, {5, 3, 12},
769 {0, 1, 8}, {0, 4, 16}, {3, 4, 17}, {3, 1, 9},
773 for (int edge = 0; edge < FRAMED_EDGE_COUNT; edge++) {
775 if (nb[nb_triplet[edge][2]])
776 edge_invisible = nb[nb_triplet[edge][0]] & nb[nb_triplet[edge][1]];
778 edge_invisible = nb[nb_triplet[edge][0]] ^ nb[nb_triplet[edge][1]];
781 drawAutoLightedCuboid(frame_edges[edge]);
784 for (int face = 0; face < 6; face++) {
788 tile = glass_tiles[face];
797 for (v3f &vertex : vertices) {
800 vertex.rotateXZBy(180); break;
802 vertex.rotateYZBy( 90); break;
804 vertex.rotateXZBy( 90); break;
806 vertex.rotateXZBy( 0); break;
808 vertex.rotateYZBy(-90); break;
810 vertex.rotateXZBy(-90); break;
813 v3s16 dir = g_6dirs[face];
814 drawQuad(vertices, dir);
817 // Optionally render internal liquid level defined by param2
818 // Liquid is textured with 1 tile defined in nodedef 'special_tiles'
819 if (param2 > 0 && f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL &&
820 f->special_tiles[0].layers[0].texture) {
821 // Internal liquid level has param2 range 0 .. 63,
822 // convert it to -0.5 .. 0.5
823 float vlev = (param2 / 63.0f) * 2.0f - 1.0f;
824 getSpecialTile(0, &tile);
825 drawAutoLightedCuboid(aabb3f(-(nb[5] ? g : b),
829 (nb[1] ? g : b) * vlev,
834 void MapblockMeshGenerator::drawAllfacesNode()
836 static const aabb3f box(-BS / 2, -BS / 2, -BS / 2, BS / 2, BS / 2, BS / 2);
838 drawAutoLightedCuboid(box);
841 void MapblockMeshGenerator::drawTorchlikeNode()
843 u8 wall = n.getWallMounted(nodedef);
846 case DWM_YP: tileindex = 1; break; // ceiling
847 case DWM_YN: tileindex = 0; break; // floor
848 default: tileindex = 2; // side (or invalid—should we care?)
850 useTile(tileindex, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
852 float size = BS / 2 * f->visual_scale;
856 v3f( size, -size, 0),
857 v3f(-size, -size, 0),
860 for (v3f &vertex : vertices) {
863 vertex.Y += -size + BS/2;
864 vertex.rotateXZBy(-45);
867 vertex.Y += size - BS/2;
868 vertex.rotateXZBy(45);
871 vertex.X += -size + BS/2;
874 vertex.X += -size + BS/2;
875 vertex.rotateXZBy(180);
878 vertex.X += -size + BS/2;
879 vertex.rotateXZBy(90);
882 vertex.X += -size + BS/2;
883 vertex.rotateXZBy(-90);
889 void MapblockMeshGenerator::drawSignlikeNode()
891 u8 wall = n.getWallMounted(nodedef);
892 useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
893 static const float offset = BS / 16;
894 float size = BS / 2 * f->visual_scale;
895 // Wall at X+ of node
897 v3f(BS / 2 - offset, size, size),
898 v3f(BS / 2 - offset, size, -size),
899 v3f(BS / 2 - offset, -size, -size),
900 v3f(BS / 2 - offset, -size, size),
903 for (v3f &vertex : vertices) {
906 vertex.rotateXYBy( 90); break;
908 vertex.rotateXYBy(-90); break;
910 vertex.rotateXZBy( 0); break;
912 vertex.rotateXZBy(180); break;
914 vertex.rotateXZBy( 90); break;
916 vertex.rotateXZBy(-90); break;
922 void MapblockMeshGenerator::drawPlantlikeQuad(float rotation, float quad_offset,
923 bool offset_top_only)
926 v3f(-scale, -BS / 2 + 2.0 * scale * plant_height, 0),
927 v3f( scale, -BS / 2 + 2.0 * scale * plant_height, 0),
928 v3f( scale, -BS / 2, 0),
929 v3f(-scale, -BS / 2, 0),
931 if (random_offset_Y) {
932 PseudoRandom yrng(face_num++ | p.X << 16 | p.Z << 8 | p.Y << 24);
933 offset.Y = -BS * ((yrng.next() % 16 / 16.0) * 0.125);
935 int offset_count = offset_top_only ? 2 : 4;
936 for (int i = 0; i < offset_count; i++)
937 vertices[i].Z += quad_offset;
939 for (v3f &vertex : vertices) {
940 vertex.rotateXZBy(rotation + rotate_degree);
943 drawQuad(vertices, v3s16(0, 0, 0), plant_height);
946 void MapblockMeshGenerator::drawPlantlike()
948 draw_style = PLANT_STYLE_CROSS;
949 scale = BS / 2 * f->visual_scale;
950 offset = v3f(0, 0, 0);
952 random_offset_Y = false;
956 switch (f->param_type_2) {
957 case CPT2_MESHOPTIONS:
958 draw_style = PlantlikeStyle(n.param2 & MO_MASK_STYLE);
959 if (n.param2 & MO_BIT_SCALE_SQRT2)
961 if (n.param2 & MO_BIT_RANDOM_OFFSET) {
962 PseudoRandom rng(p.X << 8 | p.Z | p.Y << 16);
963 offset.X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
964 offset.Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
966 if (n.param2 & MO_BIT_RANDOM_OFFSET_Y)
967 random_offset_Y = true;
971 rotate_degree = n.param2 * 2;
975 plant_height = n.param2 / 16.0;
982 switch (draw_style) {
983 case PLANT_STYLE_CROSS:
984 drawPlantlikeQuad(46);
985 drawPlantlikeQuad(-44);
988 case PLANT_STYLE_CROSS2:
989 drawPlantlikeQuad(91);
990 drawPlantlikeQuad(1);
993 case PLANT_STYLE_STAR:
994 drawPlantlikeQuad(121);
995 drawPlantlikeQuad(241);
996 drawPlantlikeQuad(1);
999 case PLANT_STYLE_HASH:
1000 drawPlantlikeQuad( 1, BS / 4);
1001 drawPlantlikeQuad( 91, BS / 4);
1002 drawPlantlikeQuad(181, BS / 4);
1003 drawPlantlikeQuad(271, BS / 4);
1006 case PLANT_STYLE_HASH2:
1007 drawPlantlikeQuad( 1, -BS / 2, true);
1008 drawPlantlikeQuad( 91, -BS / 2, true);
1009 drawPlantlikeQuad(181, -BS / 2, true);
1010 drawPlantlikeQuad(271, -BS / 2, true);
1015 void MapblockMeshGenerator::drawPlantlikeNode()
1021 void MapblockMeshGenerator::drawPlantlikeRootedNode()
1023 useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, 0, true);
1024 origin += v3f(0.0, BS, 0.0);
1026 if (data->m_smooth_lighting) {
1027 getSmoothLightFrame();
1029 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1030 light = LightPair(getInteriorLight(ntop, 1, nodedef));
1036 void MapblockMeshGenerator::drawFirelikeQuad(float rotation, float opening_angle,
1037 float offset_h, float offset_v)
1040 v3f(-scale, -BS / 2 + scale * 2, 0),
1041 v3f( scale, -BS / 2 + scale * 2, 0),
1042 v3f( scale, -BS / 2, 0),
1043 v3f(-scale, -BS / 2, 0),
1046 for (v3f &vertex : vertices) {
1047 vertex.rotateYZBy(opening_angle);
1048 vertex.Z += offset_h;
1049 vertex.rotateXZBy(rotation);
1050 vertex.Y += offset_v;
1055 void MapblockMeshGenerator::drawFirelikeNode()
1058 scale = BS / 2 * f->visual_scale;
1060 // Check for adjacent nodes
1061 bool neighbors = false;
1062 bool neighbor[6] = {0, 0, 0, 0, 0, 0};
1063 content_t current = n.getContent();
1064 for (int i = 0; i < 6; i++) {
1065 v3s16 n2p = blockpos_nodes + p + g_6dirs[i];
1066 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
1067 content_t n2c = n2.getContent();
1068 if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
1073 bool drawBasicFire = neighbor[D6D_YN] || !neighbors;
1074 bool drawBottomFire = neighbor[D6D_YP];
1076 if (drawBasicFire || neighbor[D6D_ZP])
1077 drawFirelikeQuad(0, -10, 0.4 * BS);
1078 else if (drawBottomFire)
1079 drawFirelikeQuad(0, 70, 0.47 * BS, 0.484 * BS);
1081 if (drawBasicFire || neighbor[D6D_XN])
1082 drawFirelikeQuad(90, -10, 0.4 * BS);
1083 else if (drawBottomFire)
1084 drawFirelikeQuad(90, 70, 0.47 * BS, 0.484 * BS);
1086 if (drawBasicFire || neighbor[D6D_ZN])
1087 drawFirelikeQuad(180, -10, 0.4 * BS);
1088 else if (drawBottomFire)
1089 drawFirelikeQuad(180, 70, 0.47 * BS, 0.484 * BS);
1091 if (drawBasicFire || neighbor[D6D_XP])
1092 drawFirelikeQuad(270, -10, 0.4 * BS);
1093 else if (drawBottomFire)
1094 drawFirelikeQuad(270, 70, 0.47 * BS, 0.484 * BS);
1096 if (drawBasicFire) {
1097 drawFirelikeQuad(45, 0, 0.0);
1098 drawFirelikeQuad(-45, 0, 0.0);
1102 void MapblockMeshGenerator::drawFencelikeNode()
1105 TileSpec tile_nocrack = tile;
1107 for (auto &layer : tile_nocrack.layers)
1108 layer.material_flags &= ~MATERIAL_FLAG_CRACK;
1110 // Put wood the right way around in the posts
1111 TileSpec tile_rot = tile;
1112 tile_rot.rotation = 1;
1114 static const f32 post_rad = BS / 8;
1115 static const f32 bar_rad = BS / 16;
1116 static const f32 bar_len = BS / 2 - post_rad;
1118 // The post - always present
1119 static const aabb3f post(-post_rad, -BS / 2, -post_rad,
1120 post_rad, BS / 2, post_rad);
1121 static const f32 postuv[24] = {
1122 0.375, 0.375, 0.625, 0.625,
1123 0.375, 0.375, 0.625, 0.625,
1124 0.000, 0.000, 0.250, 1.000,
1125 0.250, 0.000, 0.500, 1.000,
1126 0.500, 0.000, 0.750, 1.000,
1127 0.750, 0.000, 1.000, 1.000,
1130 drawAutoLightedCuboid(post, postuv);
1132 tile = tile_nocrack;
1134 // Now a section of fence, +X, if there's a post there
1137 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1138 const ContentFeatures *f2 = &nodedef->get(n2);
1139 if (f2->drawtype == NDT_FENCELIKE) {
1140 static const aabb3f bar_x1(BS / 2 - bar_len, BS / 4 - bar_rad, -bar_rad,
1141 BS / 2 + bar_len, BS / 4 + bar_rad, bar_rad);
1142 static const aabb3f bar_x2(BS / 2 - bar_len, -BS / 4 - bar_rad, -bar_rad,
1143 BS / 2 + bar_len, -BS / 4 + bar_rad, bar_rad);
1144 static const f32 xrailuv[24] = {
1145 0.000, 0.125, 1.000, 0.250,
1146 0.000, 0.250, 1.000, 0.375,
1147 0.375, 0.375, 0.500, 0.500,
1148 0.625, 0.625, 0.750, 0.750,
1149 0.000, 0.500, 1.000, 0.625,
1150 0.000, 0.875, 1.000, 1.000,
1152 drawAutoLightedCuboid(bar_x1, xrailuv);
1153 drawAutoLightedCuboid(bar_x2, xrailuv);
1156 // Now a section of fence, +Z, if there's a post there
1159 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1160 f2 = &nodedef->get(n2);
1161 if (f2->drawtype == NDT_FENCELIKE) {
1162 static const aabb3f bar_z1(-bar_rad, BS / 4 - bar_rad, BS / 2 - bar_len,
1163 bar_rad, BS / 4 + bar_rad, BS / 2 + bar_len);
1164 static const aabb3f bar_z2(-bar_rad, -BS / 4 - bar_rad, BS / 2 - bar_len,
1165 bar_rad, -BS / 4 + bar_rad, BS / 2 + bar_len);
1166 static const f32 zrailuv[24] = {
1167 0.1875, 0.0625, 0.3125, 0.3125, // cannot rotate; stretch
1168 0.2500, 0.0625, 0.3750, 0.3125, // for wood texture instead
1169 0.0000, 0.5625, 1.0000, 0.6875,
1170 0.0000, 0.3750, 1.0000, 0.5000,
1171 0.3750, 0.3750, 0.5000, 0.5000,
1172 0.6250, 0.6250, 0.7500, 0.7500,
1174 drawAutoLightedCuboid(bar_z1, zrailuv);
1175 drawAutoLightedCuboid(bar_z2, zrailuv);
1179 bool MapblockMeshGenerator::isSameRail(v3s16 dir)
1181 MapNode node2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
1182 if (node2.getContent() == n.getContent())
1184 const ContentFeatures &def2 = nodedef->get(node2);
1185 return ((def2.drawtype == NDT_RAILLIKE) &&
1186 (def2.getGroup(raillike_groupname) == raillike_group));
1189 void MapblockMeshGenerator::drawRaillikeNode()
1191 static const v3s16 direction[4] = {
1197 static const int slope_angle[4] = {0, 180, 90, -90};
1209 static const RailDesc rail_kinds[16] = {
1212 {straight, 0}, // . . . .
1213 {straight, 0}, // . . . +Z
1214 {straight, 0}, // . . -Z .
1215 {straight, 0}, // . . -Z +Z
1216 {straight, 90}, // . -X . .
1217 { curved, 180}, // . -X . +Z
1218 { curved, 270}, // . -X -Z .
1219 {junction, 180}, // . -X -Z +Z
1220 {straight, 90}, // +X . . .
1221 { curved, 90}, // +X . . +Z
1222 { curved, 0}, // +X . -Z .
1223 {junction, 0}, // +X . -Z +Z
1224 {straight, 90}, // +X -X . .
1225 {junction, 90}, // +X -X . +Z
1226 {junction, 270}, // +X -X -Z .
1227 { cross, 0}, // +X -X -Z +Z
1230 raillike_group = nodedef->get(n).getGroup(raillike_groupname);
1235 bool sloped = false;
1236 for (int dir = 0; dir < 4; dir++) {
1237 bool rail_above = isSameRail(direction[dir] + v3s16(0, 1, 0));
1240 angle = slope_angle[dir];
1243 isSameRail(direction[dir]) ||
1244 isSameRail(direction[dir] + v3s16(0, -1, 0)))
1249 tile_index = straight;
1251 tile_index = rail_kinds[code].tile_index;
1252 angle = rail_kinds[code].angle;
1255 useTile(tile_index, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
1257 static const float offset = BS / 64;
1258 static const float size = BS / 2;
1259 float y2 = sloped ? size : -size;
1261 v3f(-size, y2 + offset, size),
1262 v3f( size, y2 + offset, size),
1263 v3f( size, -size + offset, -size),
1264 v3f(-size, -size + offset, -size),
1267 for (v3f &vertex : vertices)
1268 vertex.rotateXZBy(angle);
1272 void MapblockMeshGenerator::drawNodeboxNode()
1274 static const v3s16 tile_dirs[6] = {
1283 // we have this order for some reason...
1284 static const v3s16 connection_dirs[6] = {
1285 v3s16( 0, 1, 0), // top
1286 v3s16( 0, -1, 0), // bottom
1287 v3s16( 0, 0, -1), // front
1288 v3s16(-1, 0, 0), // left
1289 v3s16( 0, 0, 1), // back
1290 v3s16( 1, 0, 0), // right
1294 for (int face = 0; face < 6; face++) {
1295 // Handles facedir rotation for textures
1296 getTile(tile_dirs[face], &tiles[face]);
1299 // locate possible neighboring nodes to connect to
1300 int neighbors_set = 0;
1301 if (f->node_box.type == NODEBOX_CONNECTED) {
1302 for (int dir = 0; dir != 6; dir++) {
1303 int flag = 1 << dir;
1304 v3s16 p2 = blockpos_nodes + p + connection_dirs[dir];
1305 MapNode n2 = data->m_vmanip.getNodeNoEx(p2);
1306 if (nodedef->nodeboxConnects(n, n2, flag))
1307 neighbors_set |= flag;
1311 std::vector<aabb3f> boxes;
1312 n.getNodeBoxes(nodedef, &boxes, neighbors_set);
1313 for (const auto &box : boxes)
1314 drawAutoLightedCuboid(box, nullptr, tiles, 6);
1317 void MapblockMeshGenerator::drawMeshNode()
1321 bool private_mesh; // as a grab/drop pair is not thread-safe
1323 if (f->param_type_2 == CPT2_FACEDIR ||
1324 f->param_type_2 == CPT2_COLORED_FACEDIR) {
1325 facedir = n.getFaceDir(nodedef);
1326 } else if (f->param_type_2 == CPT2_WALLMOUNTED ||
1327 f->param_type_2 == CPT2_COLORED_WALLMOUNTED) {
1328 // Convert wallmounted to 6dfacedir.
1329 // When cache enabled, it is already converted.
1330 facedir = n.getWallMounted(nodedef);
1331 if (!enable_mesh_cache)
1332 facedir = wallmounted_to_facedir[facedir];
1335 if (!data->m_smooth_lighting && f->mesh_ptr[facedir]) {
1336 // use cached meshes
1337 private_mesh = false;
1338 mesh = f->mesh_ptr[facedir];
1339 } else if (f->mesh_ptr[0]) {
1340 // no cache, clone and rotate mesh
1341 private_mesh = true;
1342 mesh = cloneMesh(f->mesh_ptr[0]);
1343 rotateMeshBy6dFacedir(mesh, facedir);
1344 recalculateBoundingBox(mesh);
1345 meshmanip->recalculateNormals(mesh, true, false);
1349 int mesh_buffer_count = mesh->getMeshBufferCount();
1350 for (int j = 0; j < mesh_buffer_count; j++) {
1352 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1353 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1354 int vertex_count = buf->getVertexCount();
1356 if (data->m_smooth_lighting) {
1357 // Mesh is always private here. So the lighting is applied to each
1358 // vertex right here.
1359 for (int k = 0; k < vertex_count; k++) {
1360 video::S3DVertex &vertex = vertices[k];
1361 vertex.Color = blendLightColor(vertex.Pos, vertex.Normal);
1362 vertex.Pos += origin;
1364 collector->append(tile, vertices, vertex_count,
1365 buf->getIndices(), buf->getIndexCount());
1367 // Don't modify the mesh, it may not be private here.
1368 // Instead, let the collector process colors, etc.
1369 collector->append(tile, vertices, vertex_count,
1370 buf->getIndices(), buf->getIndexCount(), origin,
1371 color, f->light_source);
1378 // also called when the drawtype is known but should have been pre-converted
1379 void MapblockMeshGenerator::errorUnknownDrawtype()
1381 infostream << "Got drawtype " << f->drawtype << std::endl;
1382 FATAL_ERROR("Unknown drawtype");
1385 void MapblockMeshGenerator::drawNode()
1387 // skip some drawtypes early
1388 switch (f->drawtype) {
1389 case NDT_NORMAL: // Drawn by MapBlockMesh
1390 case NDT_AIRLIKE: // Not drawn at all
1391 case NDT_LIQUID: // Drawn by MapBlockMesh
1396 origin = intToFloat(p, BS);
1397 if (data->m_smooth_lighting)
1398 getSmoothLightFrame();
1400 light = LightPair(getInteriorLight(n, 1, nodedef));
1401 switch (f->drawtype) {
1402 case NDT_FLOWINGLIQUID: drawLiquidNode(); break;
1403 case NDT_GLASSLIKE: drawGlasslikeNode(); break;
1404 case NDT_GLASSLIKE_FRAMED: drawGlasslikeFramedNode(); break;
1405 case NDT_ALLFACES: drawAllfacesNode(); break;
1406 case NDT_TORCHLIKE: drawTorchlikeNode(); break;
1407 case NDT_SIGNLIKE: drawSignlikeNode(); break;
1408 case NDT_PLANTLIKE: drawPlantlikeNode(); break;
1409 case NDT_PLANTLIKE_ROOTED: drawPlantlikeRootedNode(); break;
1410 case NDT_FIRELIKE: drawFirelikeNode(); break;
1411 case NDT_FENCELIKE: drawFencelikeNode(); break;
1412 case NDT_RAILLIKE: drawRaillikeNode(); break;
1413 case NDT_NODEBOX: drawNodeboxNode(); break;
1414 case NDT_MESH: drawMeshNode(); break;
1415 default: errorUnknownDrawtype(); break;
1420 TODO: Fix alpha blending for special nodes
1421 Currently only the last element rendered is blended correct
1423 void MapblockMeshGenerator::generate()
1425 for (p.Z = 0; p.Z < MAP_BLOCKSIZE; p.Z++)
1426 for (p.Y = 0; p.Y < MAP_BLOCKSIZE; p.Y++)
1427 for (p.X = 0; p.X < MAP_BLOCKSIZE; p.X++) {
1428 n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1429 f = &nodedef->get(n);
1434 void MapblockMeshGenerator::renderSingle(content_t node)
1437 n = MapNode(node, 0xff, 0x00);
1438 f = &nodedef->get(n);