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.rotateXZBy(-45); break;
865 vertex.rotateXZBy( 45); break;
867 vertex.rotateXZBy( 0); break;
869 vertex.rotateXZBy(180); break;
871 vertex.rotateXZBy( 90); break;
873 vertex.rotateXZBy(-90); break;
879 void MapblockMeshGenerator::drawSignlikeNode()
881 u8 wall = n.getWallMounted(nodedef);
882 useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
883 static const float offset = BS / 16;
884 float size = BS / 2 * f->visual_scale;
885 // Wall at X+ of node
887 v3f(BS / 2 - offset, size, size),
888 v3f(BS / 2 - offset, size, -size),
889 v3f(BS / 2 - offset, -size, -size),
890 v3f(BS / 2 - offset, -size, size),
893 for (v3f &vertex : vertices) {
896 vertex.rotateXYBy( 90); break;
898 vertex.rotateXYBy(-90); break;
900 vertex.rotateXZBy( 0); break;
902 vertex.rotateXZBy(180); break;
904 vertex.rotateXZBy( 90); break;
906 vertex.rotateXZBy(-90); break;
912 void MapblockMeshGenerator::drawPlantlikeQuad(float rotation, float quad_offset,
913 bool offset_top_only)
916 v3f(-scale, -BS / 2 + 2.0 * scale * plant_height, 0),
917 v3f( scale, -BS / 2 + 2.0 * scale * plant_height, 0),
918 v3f( scale, -BS / 2, 0),
919 v3f(-scale, -BS / 2, 0),
921 if (random_offset_Y) {
922 PseudoRandom yrng(face_num++ | p.X << 16 | p.Z << 8 | p.Y << 24);
923 offset.Y = -BS * ((yrng.next() % 16 / 16.0) * 0.125);
925 int offset_count = offset_top_only ? 2 : 4;
926 for (int i = 0; i < offset_count; i++)
927 vertices[i].Z += quad_offset;
929 for (v3f &vertex : vertices) {
930 vertex.rotateXZBy(rotation + rotate_degree);
933 drawQuad(vertices, v3s16(0, 0, 0), plant_height);
936 void MapblockMeshGenerator::drawPlantlike()
938 draw_style = PLANT_STYLE_CROSS;
939 scale = BS / 2 * f->visual_scale;
940 offset = v3f(0, 0, 0);
942 random_offset_Y = false;
946 switch (f->param_type_2) {
947 case CPT2_MESHOPTIONS:
948 draw_style = PlantlikeStyle(n.param2 & MO_MASK_STYLE);
949 if (n.param2 & MO_BIT_SCALE_SQRT2)
951 if (n.param2 & MO_BIT_RANDOM_OFFSET) {
952 PseudoRandom rng(p.X << 8 | p.Z | p.Y << 16);
953 offset.X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
954 offset.Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
956 if (n.param2 & MO_BIT_RANDOM_OFFSET_Y)
957 random_offset_Y = true;
961 rotate_degree = n.param2 * 2;
965 plant_height = n.param2 / 16.0;
972 switch (draw_style) {
973 case PLANT_STYLE_CROSS:
974 drawPlantlikeQuad(46);
975 drawPlantlikeQuad(-44);
978 case PLANT_STYLE_CROSS2:
979 drawPlantlikeQuad(91);
980 drawPlantlikeQuad(1);
983 case PLANT_STYLE_STAR:
984 drawPlantlikeQuad(121);
985 drawPlantlikeQuad(241);
986 drawPlantlikeQuad(1);
989 case PLANT_STYLE_HASH:
990 drawPlantlikeQuad( 1, BS / 4);
991 drawPlantlikeQuad( 91, BS / 4);
992 drawPlantlikeQuad(181, BS / 4);
993 drawPlantlikeQuad(271, BS / 4);
996 case PLANT_STYLE_HASH2:
997 drawPlantlikeQuad( 1, -BS / 2, true);
998 drawPlantlikeQuad( 91, -BS / 2, true);
999 drawPlantlikeQuad(181, -BS / 2, true);
1000 drawPlantlikeQuad(271, -BS / 2, true);
1005 void MapblockMeshGenerator::drawPlantlikeNode()
1011 void MapblockMeshGenerator::drawPlantlikeRootedNode()
1013 useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, 0, true);
1014 origin += v3f(0.0, BS, 0.0);
1016 if (data->m_smooth_lighting) {
1017 getSmoothLightFrame();
1019 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1020 light = LightPair(getInteriorLight(ntop, 1, nodedef));
1026 void MapblockMeshGenerator::drawFirelikeQuad(float rotation, float opening_angle,
1027 float offset_h, float offset_v)
1030 v3f(-scale, -BS / 2 + scale * 2, 0),
1031 v3f( scale, -BS / 2 + scale * 2, 0),
1032 v3f( scale, -BS / 2, 0),
1033 v3f(-scale, -BS / 2, 0),
1036 for (v3f &vertex : vertices) {
1037 vertex.rotateYZBy(opening_angle);
1038 vertex.Z += offset_h;
1039 vertex.rotateXZBy(rotation);
1040 vertex.Y += offset_v;
1045 void MapblockMeshGenerator::drawFirelikeNode()
1048 scale = BS / 2 * f->visual_scale;
1050 // Check for adjacent nodes
1051 bool neighbors = false;
1052 bool neighbor[6] = {0, 0, 0, 0, 0, 0};
1053 content_t current = n.getContent();
1054 for (int i = 0; i < 6; i++) {
1055 v3s16 n2p = blockpos_nodes + p + g_6dirs[i];
1056 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
1057 content_t n2c = n2.getContent();
1058 if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
1063 bool drawBasicFire = neighbor[D6D_YN] || !neighbors;
1064 bool drawBottomFire = neighbor[D6D_YP];
1066 if (drawBasicFire || neighbor[D6D_ZP])
1067 drawFirelikeQuad(0, -10, 0.4 * BS);
1068 else if (drawBottomFire)
1069 drawFirelikeQuad(0, 70, 0.47 * BS, 0.484 * BS);
1071 if (drawBasicFire || neighbor[D6D_XN])
1072 drawFirelikeQuad(90, -10, 0.4 * BS);
1073 else if (drawBottomFire)
1074 drawFirelikeQuad(90, 70, 0.47 * BS, 0.484 * BS);
1076 if (drawBasicFire || neighbor[D6D_ZN])
1077 drawFirelikeQuad(180, -10, 0.4 * BS);
1078 else if (drawBottomFire)
1079 drawFirelikeQuad(180, 70, 0.47 * BS, 0.484 * BS);
1081 if (drawBasicFire || neighbor[D6D_XP])
1082 drawFirelikeQuad(270, -10, 0.4 * BS);
1083 else if (drawBottomFire)
1084 drawFirelikeQuad(270, 70, 0.47 * BS, 0.484 * BS);
1086 if (drawBasicFire) {
1087 drawFirelikeQuad(45, 0, 0.0);
1088 drawFirelikeQuad(-45, 0, 0.0);
1092 void MapblockMeshGenerator::drawFencelikeNode()
1095 TileSpec tile_nocrack = tile;
1097 for (auto &layer : tile_nocrack.layers)
1098 layer.material_flags &= ~MATERIAL_FLAG_CRACK;
1100 // Put wood the right way around in the posts
1101 TileSpec tile_rot = tile;
1102 tile_rot.rotation = 1;
1104 static const f32 post_rad = BS / 8;
1105 static const f32 bar_rad = BS / 16;
1106 static const f32 bar_len = BS / 2 - post_rad;
1108 // The post - always present
1109 static const aabb3f post(-post_rad, -BS / 2, -post_rad,
1110 post_rad, BS / 2, post_rad);
1111 static const f32 postuv[24] = {
1112 0.375, 0.375, 0.625, 0.625,
1113 0.375, 0.375, 0.625, 0.625,
1114 0.000, 0.000, 0.250, 1.000,
1115 0.250, 0.000, 0.500, 1.000,
1116 0.500, 0.000, 0.750, 1.000,
1117 0.750, 0.000, 1.000, 1.000,
1120 drawAutoLightedCuboid(post, postuv);
1122 tile = tile_nocrack;
1124 // Now a section of fence, +X, if there's a post there
1127 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1128 const ContentFeatures *f2 = &nodedef->get(n2);
1129 if (f2->drawtype == NDT_FENCELIKE) {
1130 static const aabb3f bar_x1(BS / 2 - bar_len, BS / 4 - bar_rad, -bar_rad,
1131 BS / 2 + bar_len, BS / 4 + bar_rad, bar_rad);
1132 static const aabb3f bar_x2(BS / 2 - bar_len, -BS / 4 - bar_rad, -bar_rad,
1133 BS / 2 + bar_len, -BS / 4 + bar_rad, bar_rad);
1134 static const f32 xrailuv[24] = {
1135 0.000, 0.125, 1.000, 0.250,
1136 0.000, 0.250, 1.000, 0.375,
1137 0.375, 0.375, 0.500, 0.500,
1138 0.625, 0.625, 0.750, 0.750,
1139 0.000, 0.500, 1.000, 0.625,
1140 0.000, 0.875, 1.000, 1.000,
1142 drawAutoLightedCuboid(bar_x1, xrailuv);
1143 drawAutoLightedCuboid(bar_x2, xrailuv);
1146 // Now a section of fence, +Z, if there's a post there
1149 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1150 f2 = &nodedef->get(n2);
1151 if (f2->drawtype == NDT_FENCELIKE) {
1152 static const aabb3f bar_z1(-bar_rad, BS / 4 - bar_rad, BS / 2 - bar_len,
1153 bar_rad, BS / 4 + bar_rad, BS / 2 + bar_len);
1154 static const aabb3f bar_z2(-bar_rad, -BS / 4 - bar_rad, BS / 2 - bar_len,
1155 bar_rad, -BS / 4 + bar_rad, BS / 2 + bar_len);
1156 static const f32 zrailuv[24] = {
1157 0.1875, 0.0625, 0.3125, 0.3125, // cannot rotate; stretch
1158 0.2500, 0.0625, 0.3750, 0.3125, // for wood texture instead
1159 0.0000, 0.5625, 1.0000, 0.6875,
1160 0.0000, 0.3750, 1.0000, 0.5000,
1161 0.3750, 0.3750, 0.5000, 0.5000,
1162 0.6250, 0.6250, 0.7500, 0.7500,
1164 drawAutoLightedCuboid(bar_z1, zrailuv);
1165 drawAutoLightedCuboid(bar_z2, zrailuv);
1169 bool MapblockMeshGenerator::isSameRail(v3s16 dir)
1171 MapNode node2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
1172 if (node2.getContent() == n.getContent())
1174 const ContentFeatures &def2 = nodedef->get(node2);
1175 return ((def2.drawtype == NDT_RAILLIKE) &&
1176 (def2.getGroup(raillike_groupname) == raillike_group));
1179 void MapblockMeshGenerator::drawRaillikeNode()
1181 static const v3s16 direction[4] = {
1187 static const int slope_angle[4] = {0, 180, 90, -90};
1199 static const RailDesc rail_kinds[16] = {
1202 {straight, 0}, // . . . .
1203 {straight, 0}, // . . . +Z
1204 {straight, 0}, // . . -Z .
1205 {straight, 0}, // . . -Z +Z
1206 {straight, 90}, // . -X . .
1207 { curved, 180}, // . -X . +Z
1208 { curved, 270}, // . -X -Z .
1209 {junction, 180}, // . -X -Z +Z
1210 {straight, 90}, // +X . . .
1211 { curved, 90}, // +X . . +Z
1212 { curved, 0}, // +X . -Z .
1213 {junction, 0}, // +X . -Z +Z
1214 {straight, 90}, // +X -X . .
1215 {junction, 90}, // +X -X . +Z
1216 {junction, 270}, // +X -X -Z .
1217 { cross, 0}, // +X -X -Z +Z
1220 raillike_group = nodedef->get(n).getGroup(raillike_groupname);
1225 bool sloped = false;
1226 for (int dir = 0; dir < 4; dir++) {
1227 bool rail_above = isSameRail(direction[dir] + v3s16(0, 1, 0));
1230 angle = slope_angle[dir];
1233 isSameRail(direction[dir]) ||
1234 isSameRail(direction[dir] + v3s16(0, -1, 0)))
1239 tile_index = straight;
1241 tile_index = rail_kinds[code].tile_index;
1242 angle = rail_kinds[code].angle;
1245 useTile(tile_index, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
1247 static const float offset = BS / 64;
1248 static const float size = BS / 2;
1249 float y2 = sloped ? size : -size;
1251 v3f(-size, y2 + offset, size),
1252 v3f( size, y2 + offset, size),
1253 v3f( size, -size + offset, -size),
1254 v3f(-size, -size + offset, -size),
1257 for (v3f &vertex : vertices)
1258 vertex.rotateXZBy(angle);
1262 void MapblockMeshGenerator::drawNodeboxNode()
1264 static const v3s16 tile_dirs[6] = {
1273 // we have this order for some reason...
1274 static const v3s16 connection_dirs[6] = {
1275 v3s16( 0, 1, 0), // top
1276 v3s16( 0, -1, 0), // bottom
1277 v3s16( 0, 0, -1), // front
1278 v3s16(-1, 0, 0), // left
1279 v3s16( 0, 0, 1), // back
1280 v3s16( 1, 0, 0), // right
1284 for (int face = 0; face < 6; face++) {
1285 // Handles facedir rotation for textures
1286 getTile(tile_dirs[face], &tiles[face]);
1289 // locate possible neighboring nodes to connect to
1290 int neighbors_set = 0;
1291 if (f->node_box.type == NODEBOX_CONNECTED) {
1292 for (int dir = 0; dir != 6; dir++) {
1293 int flag = 1 << dir;
1294 v3s16 p2 = blockpos_nodes + p + connection_dirs[dir];
1295 MapNode n2 = data->m_vmanip.getNodeNoEx(p2);
1296 if (nodedef->nodeboxConnects(n, n2, flag))
1297 neighbors_set |= flag;
1301 std::vector<aabb3f> boxes;
1302 n.getNodeBoxes(nodedef, &boxes, neighbors_set);
1303 for (const auto &box : boxes)
1304 drawAutoLightedCuboid(box, nullptr, tiles, 6);
1307 void MapblockMeshGenerator::drawMeshNode()
1311 bool private_mesh; // as a grab/drop pair is not thread-safe
1313 if (f->param_type_2 == CPT2_FACEDIR ||
1314 f->param_type_2 == CPT2_COLORED_FACEDIR) {
1315 facedir = n.getFaceDir(nodedef);
1316 } else if (f->param_type_2 == CPT2_WALLMOUNTED ||
1317 f->param_type_2 == CPT2_COLORED_WALLMOUNTED) {
1318 // Convert wallmounted to 6dfacedir.
1319 // When cache enabled, it is already converted.
1320 facedir = n.getWallMounted(nodedef);
1321 if (!enable_mesh_cache)
1322 facedir = wallmounted_to_facedir[facedir];
1325 if (!data->m_smooth_lighting && f->mesh_ptr[facedir]) {
1326 // use cached meshes
1327 private_mesh = false;
1328 mesh = f->mesh_ptr[facedir];
1329 } else if (f->mesh_ptr[0]) {
1330 // no cache, clone and rotate mesh
1331 private_mesh = true;
1332 mesh = cloneMesh(f->mesh_ptr[0]);
1333 rotateMeshBy6dFacedir(mesh, facedir);
1334 recalculateBoundingBox(mesh);
1335 meshmanip->recalculateNormals(mesh, true, false);
1339 int mesh_buffer_count = mesh->getMeshBufferCount();
1340 for (int j = 0; j < mesh_buffer_count; j++) {
1342 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1343 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1344 int vertex_count = buf->getVertexCount();
1346 if (data->m_smooth_lighting) {
1347 // Mesh is always private here. So the lighting is applied to each
1348 // vertex right here.
1349 for (int k = 0; k < vertex_count; k++) {
1350 video::S3DVertex &vertex = vertices[k];
1351 vertex.Color = blendLightColor(vertex.Pos, vertex.Normal);
1352 vertex.Pos += origin;
1354 collector->append(tile, vertices, vertex_count,
1355 buf->getIndices(), buf->getIndexCount());
1357 // Don't modify the mesh, it may not be private here.
1358 // Instead, let the collector process colors, etc.
1359 collector->append(tile, vertices, vertex_count,
1360 buf->getIndices(), buf->getIndexCount(), origin,
1361 color, f->light_source);
1368 // also called when the drawtype is known but should have been pre-converted
1369 void MapblockMeshGenerator::errorUnknownDrawtype()
1371 infostream << "Got drawtype " << f->drawtype << std::endl;
1372 FATAL_ERROR("Unknown drawtype");
1375 void MapblockMeshGenerator::drawNode()
1377 // skip some drawtypes early
1378 switch (f->drawtype) {
1379 case NDT_NORMAL: // Drawn by MapBlockMesh
1380 case NDT_AIRLIKE: // Not drawn at all
1381 case NDT_LIQUID: // Drawn by MapBlockMesh
1386 origin = intToFloat(p, BS);
1387 if (data->m_smooth_lighting)
1388 getSmoothLightFrame();
1390 light = LightPair(getInteriorLight(n, 1, nodedef));
1391 switch (f->drawtype) {
1392 case NDT_FLOWINGLIQUID: drawLiquidNode(); break;
1393 case NDT_GLASSLIKE: drawGlasslikeNode(); break;
1394 case NDT_GLASSLIKE_FRAMED: drawGlasslikeFramedNode(); break;
1395 case NDT_ALLFACES: drawAllfacesNode(); break;
1396 case NDT_TORCHLIKE: drawTorchlikeNode(); break;
1397 case NDT_SIGNLIKE: drawSignlikeNode(); break;
1398 case NDT_PLANTLIKE: drawPlantlikeNode(); break;
1399 case NDT_PLANTLIKE_ROOTED: drawPlantlikeRootedNode(); break;
1400 case NDT_FIRELIKE: drawFirelikeNode(); break;
1401 case NDT_FENCELIKE: drawFencelikeNode(); break;
1402 case NDT_RAILLIKE: drawRaillikeNode(); break;
1403 case NDT_NODEBOX: drawNodeboxNode(); break;
1404 case NDT_MESH: drawMeshNode(); break;
1405 default: errorUnknownDrawtype(); break;
1410 TODO: Fix alpha blending for special nodes
1411 Currently only the last element rendered is blended correct
1413 void MapblockMeshGenerator::generate()
1415 for (p.Z = 0; p.Z < MAP_BLOCKSIZE; p.Z++)
1416 for (p.Y = 0; p.Y < MAP_BLOCKSIZE; p.Y++)
1417 for (p.X = 0; p.X < MAP_BLOCKSIZE; p.X++) {
1418 n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1419 f = &nodedef->get(n);
1424 void MapblockMeshGenerator::renderSingle(content_t node)
1427 n = MapNode(node, 0xff, 0x00);
1428 f = &nodedef->get(n);