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 TileSpec glass_tiles[6];
701 if (tiles[1].layers[0].texture &&
702 tiles[2].layers[0].texture &&
703 tiles[3].layers[0].texture) {
704 glass_tiles[0] = tiles[4];
705 glass_tiles[1] = tiles[0];
706 glass_tiles[2] = tiles[4];
707 glass_tiles[3] = tiles[4];
708 glass_tiles[4] = tiles[3];
709 glass_tiles[5] = tiles[4];
711 for (auto &glass_tile : glass_tiles)
712 glass_tile = tiles[4];
715 u8 param2 = n.getParam2();
716 bool H_merge = !(param2 & 128);
717 bool V_merge = !(param2 & 64);
720 static const float a = BS / 2;
721 static const float g = a - 0.003;
722 static const float b = .876 * ( BS / 2 );
724 static const aabb3f frame_edges[FRAMED_EDGE_COUNT] = {
725 aabb3f( b, b, -a, a, a, a), // y+
726 aabb3f(-a, b, -a, -b, a, a), // y+
727 aabb3f( b, -a, -a, a, -b, a), // y-
728 aabb3f(-a, -a, -a, -b, -b, a), // y-
729 aabb3f( b, -a, b, a, a, a), // x+
730 aabb3f( b, -a, -a, a, a, -b), // x+
731 aabb3f(-a, -a, b, -b, a, a), // x-
732 aabb3f(-a, -a, -a, -b, a, -b), // x-
733 aabb3f(-a, b, b, a, a, a), // z+
734 aabb3f(-a, -a, b, a, -b, a), // z+
735 aabb3f(-a, -a, -a, a, -b, -b), // z-
736 aabb3f(-a, b, -a, a, a, -b), // z-
738 static const aabb3f glass_faces[6] = {
739 aabb3f(-g, -g, g, g, g, g), // z+
740 aabb3f(-g, g, -g, g, g, g), // y+
741 aabb3f( g, -g, -g, g, g, g), // x+
742 aabb3f(-g, -g, -g, g, g, -g), // z-
743 aabb3f(-g, -g, -g, g, -g, g), // y-
744 aabb3f(-g, -g, -g, -g, g, g), // x-
747 // tables of neighbour (connect if same type and merge allowed),
748 // checked with g_26dirs
750 // 1 = connect, 0 = face visible
751 bool nb[FRAMED_NEIGHBOR_COUNT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
754 static const bool check_nb_vertical [FRAMED_NEIGHBOR_COUNT] = {0,1,0,0,1,0, 0,0,0,0, 0,0,0,0, 0,0,0,0};
755 static const bool check_nb_horizontal [FRAMED_NEIGHBOR_COUNT] = {1,0,1,1,0,1, 0,0,0,0, 1,1,1,1, 0,0,0,0};
756 static const bool check_nb_all [FRAMED_NEIGHBOR_COUNT] = {1,1,1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1};
757 const bool *check_nb = check_nb_all;
759 // neighbours checks for frames visibility
760 if (H_merge || V_merge) {
762 check_nb = check_nb_vertical; // vertical-only merge
764 check_nb = check_nb_horizontal; // horizontal-only merge
765 content_t current = n.getContent();
766 for (int i = 0; i < FRAMED_NEIGHBOR_COUNT; i++) {
769 v3s16 n2p = blockpos_nodes + p + g_26dirs[i];
770 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
771 content_t n2c = n2.getContent();
779 static const u8 nb_triplet[FRAMED_EDGE_COUNT][3] = {
780 {1, 2, 7}, {1, 5, 6}, {4, 2, 15}, {4, 5, 14},
781 {2, 0, 11}, {2, 3, 13}, {5, 0, 10}, {5, 3, 12},
782 {0, 1, 8}, {0, 4, 16}, {3, 4, 17}, {3, 1, 9},
786 for (int edge = 0; edge < FRAMED_EDGE_COUNT; edge++) {
788 if (nb[nb_triplet[edge][2]])
789 edge_invisible = nb[nb_triplet[edge][0]] & nb[nb_triplet[edge][1]];
791 edge_invisible = nb[nb_triplet[edge][0]] ^ nb[nb_triplet[edge][1]];
794 drawAutoLightedCuboid(frame_edges[edge]);
797 for (int face = 0; face < 6; face++) {
800 tile = glass_tiles[face];
801 drawAutoLightedCuboid(glass_faces[face]);
804 // Optionally render internal liquid level defined by param2
805 // Liquid is textured with 1 tile defined in nodedef 'special_tiles'
806 if (param2 > 0 && f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL &&
807 f->special_tiles[0].layers[0].texture) {
808 // Internal liquid level has param2 range 0 .. 63,
809 // convert it to -0.5 .. 0.5
810 float vlev = (param2 / 63.0) * 2.0 - 1.0;
811 getSpecialTile(0, &tile);
812 drawAutoLightedCuboid(aabb3f(-(nb[5] ? g : b),
816 (nb[1] ? g : b) * vlev,
821 void MapblockMeshGenerator::drawAllfacesNode()
823 static const aabb3f box(-BS / 2, -BS / 2, -BS / 2, BS / 2, BS / 2, BS / 2);
825 drawAutoLightedCuboid(box);
828 void MapblockMeshGenerator::drawTorchlikeNode()
830 u8 wall = n.getWallMounted(nodedef);
833 case DWM_YP: tileindex = 1; break; // ceiling
834 case DWM_YN: tileindex = 0; break; // floor
835 default: tileindex = 2; // side (or invalid—should we care?)
837 useTile(tileindex, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
839 float size = BS / 2 * f->visual_scale;
843 v3f( size, -size, 0),
844 v3f(-size, -size, 0),
847 for (v3f &vertex : vertices) {
850 vertex.rotateXZBy(-45); break;
852 vertex.rotateXZBy( 45); break;
854 vertex.rotateXZBy( 0); break;
856 vertex.rotateXZBy(180); break;
858 vertex.rotateXZBy( 90); break;
860 vertex.rotateXZBy(-90); break;
866 void MapblockMeshGenerator::drawSignlikeNode()
868 u8 wall = n.getWallMounted(nodedef);
869 useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
870 static const float offset = BS / 16;
871 float size = BS / 2 * f->visual_scale;
872 // Wall at X+ of node
874 v3f(BS / 2 - offset, size, size),
875 v3f(BS / 2 - offset, size, -size),
876 v3f(BS / 2 - offset, -size, -size),
877 v3f(BS / 2 - offset, -size, size),
880 for (v3f &vertex : vertices) {
883 vertex.rotateXYBy( 90); break;
885 vertex.rotateXYBy(-90); break;
887 vertex.rotateXZBy( 0); break;
889 vertex.rotateXZBy(180); break;
891 vertex.rotateXZBy( 90); break;
893 vertex.rotateXZBy(-90); break;
899 void MapblockMeshGenerator::drawPlantlikeQuad(float rotation, float quad_offset,
900 bool offset_top_only)
903 v3f(-scale, -BS / 2 + 2.0 * scale * plant_height, 0),
904 v3f( scale, -BS / 2 + 2.0 * scale * plant_height, 0),
905 v3f( scale, -BS / 2, 0),
906 v3f(-scale, -BS / 2, 0),
908 if (random_offset_Y) {
909 PseudoRandom yrng(face_num++ | p.X << 16 | p.Z << 8 | p.Y << 24);
910 offset.Y = -BS * ((yrng.next() % 16 / 16.0) * 0.125);
912 int offset_count = offset_top_only ? 2 : 4;
913 for (int i = 0; i < offset_count; i++)
914 vertices[i].Z += quad_offset;
916 for (v3f &vertex : vertices) {
917 vertex.rotateXZBy(rotation + rotate_degree);
920 drawQuad(vertices, v3s16(0, 0, 0), plant_height);
923 void MapblockMeshGenerator::drawPlantlike()
925 draw_style = PLANT_STYLE_CROSS;
926 scale = BS / 2 * f->visual_scale;
927 offset = v3f(0, 0, 0);
929 random_offset_Y = false;
933 switch (f->param_type_2) {
934 case CPT2_MESHOPTIONS:
935 draw_style = PlantlikeStyle(n.param2 & MO_MASK_STYLE);
936 if (n.param2 & MO_BIT_SCALE_SQRT2)
938 if (n.param2 & MO_BIT_RANDOM_OFFSET) {
939 PseudoRandom rng(p.X << 8 | p.Z | p.Y << 16);
940 offset.X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
941 offset.Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
943 if (n.param2 & MO_BIT_RANDOM_OFFSET_Y)
944 random_offset_Y = true;
948 rotate_degree = n.param2 * 2;
952 plant_height = n.param2 / 16.0;
959 switch (draw_style) {
960 case PLANT_STYLE_CROSS:
961 drawPlantlikeQuad(46);
962 drawPlantlikeQuad(-44);
965 case PLANT_STYLE_CROSS2:
966 drawPlantlikeQuad(91);
967 drawPlantlikeQuad(1);
970 case PLANT_STYLE_STAR:
971 drawPlantlikeQuad(121);
972 drawPlantlikeQuad(241);
973 drawPlantlikeQuad(1);
976 case PLANT_STYLE_HASH:
977 drawPlantlikeQuad( 1, BS / 4);
978 drawPlantlikeQuad( 91, BS / 4);
979 drawPlantlikeQuad(181, BS / 4);
980 drawPlantlikeQuad(271, BS / 4);
983 case PLANT_STYLE_HASH2:
984 drawPlantlikeQuad( 1, -BS / 2, true);
985 drawPlantlikeQuad( 91, -BS / 2, true);
986 drawPlantlikeQuad(181, -BS / 2, true);
987 drawPlantlikeQuad(271, -BS / 2, true);
992 void MapblockMeshGenerator::drawPlantlikeNode()
998 void MapblockMeshGenerator::drawPlantlikeRootedNode()
1000 useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, 0, true);
1001 origin += v3f(0.0, BS, 0.0);
1003 if (data->m_smooth_lighting) {
1004 getSmoothLightFrame();
1006 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1007 light = LightPair(getInteriorLight(ntop, 1, nodedef));
1013 void MapblockMeshGenerator::drawFirelikeQuad(float rotation, float opening_angle,
1014 float offset_h, float offset_v)
1017 v3f(-scale, -BS / 2 + scale * 2, 0),
1018 v3f( scale, -BS / 2 + scale * 2, 0),
1019 v3f( scale, -BS / 2, 0),
1020 v3f(-scale, -BS / 2, 0),
1023 for (v3f &vertex : vertices) {
1024 vertex.rotateYZBy(opening_angle);
1025 vertex.Z += offset_h;
1026 vertex.rotateXZBy(rotation);
1027 vertex.Y += offset_v;
1032 void MapblockMeshGenerator::drawFirelikeNode()
1035 scale = BS / 2 * f->visual_scale;
1037 // Check for adjacent nodes
1038 bool neighbors = false;
1039 bool neighbor[6] = {0, 0, 0, 0, 0, 0};
1040 content_t current = n.getContent();
1041 for (int i = 0; i < 6; i++) {
1042 v3s16 n2p = blockpos_nodes + p + g_6dirs[i];
1043 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
1044 content_t n2c = n2.getContent();
1045 if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
1050 bool drawBasicFire = neighbor[D6D_YN] || !neighbors;
1051 bool drawBottomFire = neighbor[D6D_YP];
1053 if (drawBasicFire || neighbor[D6D_ZP])
1054 drawFirelikeQuad(0, -10, 0.4 * BS);
1055 else if (drawBottomFire)
1056 drawFirelikeQuad(0, 70, 0.47 * BS, 0.484 * BS);
1058 if (drawBasicFire || neighbor[D6D_XN])
1059 drawFirelikeQuad(90, -10, 0.4 * BS);
1060 else if (drawBottomFire)
1061 drawFirelikeQuad(90, 70, 0.47 * BS, 0.484 * BS);
1063 if (drawBasicFire || neighbor[D6D_ZN])
1064 drawFirelikeQuad(180, -10, 0.4 * BS);
1065 else if (drawBottomFire)
1066 drawFirelikeQuad(180, 70, 0.47 * BS, 0.484 * BS);
1068 if (drawBasicFire || neighbor[D6D_XP])
1069 drawFirelikeQuad(270, -10, 0.4 * BS);
1070 else if (drawBottomFire)
1071 drawFirelikeQuad(270, 70, 0.47 * BS, 0.484 * BS);
1073 if (drawBasicFire) {
1074 drawFirelikeQuad(45, 0, 0.0);
1075 drawFirelikeQuad(-45, 0, 0.0);
1079 void MapblockMeshGenerator::drawFencelikeNode()
1082 TileSpec tile_nocrack = tile;
1084 for (auto &layer : tile_nocrack.layers)
1085 layer.material_flags &= ~MATERIAL_FLAG_CRACK;
1087 // Put wood the right way around in the posts
1088 TileSpec tile_rot = tile;
1089 tile_rot.rotation = 1;
1091 static const f32 post_rad = BS / 8;
1092 static const f32 bar_rad = BS / 16;
1093 static const f32 bar_len = BS / 2 - post_rad;
1095 // The post - always present
1096 static const aabb3f post(-post_rad, -BS / 2, -post_rad,
1097 post_rad, BS / 2, post_rad);
1098 static const f32 postuv[24] = {
1099 0.375, 0.375, 0.625, 0.625,
1100 0.375, 0.375, 0.625, 0.625,
1101 0.000, 0.000, 0.250, 1.000,
1102 0.250, 0.000, 0.500, 1.000,
1103 0.500, 0.000, 0.750, 1.000,
1104 0.750, 0.000, 1.000, 1.000,
1107 drawAutoLightedCuboid(post, postuv);
1109 tile = tile_nocrack;
1111 // Now a section of fence, +X, if there's a post there
1114 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1115 const ContentFeatures *f2 = &nodedef->get(n2);
1116 if (f2->drawtype == NDT_FENCELIKE) {
1117 static const aabb3f bar_x1(BS / 2 - bar_len, BS / 4 - bar_rad, -bar_rad,
1118 BS / 2 + bar_len, BS / 4 + bar_rad, bar_rad);
1119 static const aabb3f bar_x2(BS / 2 - bar_len, -BS / 4 - bar_rad, -bar_rad,
1120 BS / 2 + bar_len, -BS / 4 + bar_rad, bar_rad);
1121 static const f32 xrailuv[24] = {
1122 0.000, 0.125, 1.000, 0.250,
1123 0.000, 0.250, 1.000, 0.375,
1124 0.375, 0.375, 0.500, 0.500,
1125 0.625, 0.625, 0.750, 0.750,
1126 0.000, 0.500, 1.000, 0.625,
1127 0.000, 0.875, 1.000, 1.000,
1129 drawAutoLightedCuboid(bar_x1, xrailuv);
1130 drawAutoLightedCuboid(bar_x2, xrailuv);
1133 // Now a section of fence, +Z, if there's a post there
1136 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1137 f2 = &nodedef->get(n2);
1138 if (f2->drawtype == NDT_FENCELIKE) {
1139 static const aabb3f bar_z1(-bar_rad, BS / 4 - bar_rad, BS / 2 - bar_len,
1140 bar_rad, BS / 4 + bar_rad, BS / 2 + bar_len);
1141 static const aabb3f bar_z2(-bar_rad, -BS / 4 - bar_rad, BS / 2 - bar_len,
1142 bar_rad, -BS / 4 + bar_rad, BS / 2 + bar_len);
1143 static const f32 zrailuv[24] = {
1144 0.1875, 0.0625, 0.3125, 0.3125, // cannot rotate; stretch
1145 0.2500, 0.0625, 0.3750, 0.3125, // for wood texture instead
1146 0.0000, 0.5625, 1.0000, 0.6875,
1147 0.0000, 0.3750, 1.0000, 0.5000,
1148 0.3750, 0.3750, 0.5000, 0.5000,
1149 0.6250, 0.6250, 0.7500, 0.7500,
1151 drawAutoLightedCuboid(bar_z1, zrailuv);
1152 drawAutoLightedCuboid(bar_z2, zrailuv);
1156 bool MapblockMeshGenerator::isSameRail(v3s16 dir)
1158 MapNode node2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
1159 if (node2.getContent() == n.getContent())
1161 const ContentFeatures &def2 = nodedef->get(node2);
1162 return ((def2.drawtype == NDT_RAILLIKE) &&
1163 (def2.getGroup(raillike_groupname) == raillike_group));
1166 void MapblockMeshGenerator::drawRaillikeNode()
1168 static const v3s16 direction[4] = {
1174 static const int slope_angle[4] = {0, 180, 90, -90};
1186 static const RailDesc rail_kinds[16] = {
1189 {straight, 0}, // . . . .
1190 {straight, 0}, // . . . +Z
1191 {straight, 0}, // . . -Z .
1192 {straight, 0}, // . . -Z +Z
1193 {straight, 90}, // . -X . .
1194 { curved, 180}, // . -X . +Z
1195 { curved, 270}, // . -X -Z .
1196 {junction, 180}, // . -X -Z +Z
1197 {straight, 90}, // +X . . .
1198 { curved, 90}, // +X . . +Z
1199 { curved, 0}, // +X . -Z .
1200 {junction, 0}, // +X . -Z +Z
1201 {straight, 90}, // +X -X . .
1202 {junction, 90}, // +X -X . +Z
1203 {junction, 270}, // +X -X -Z .
1204 { cross, 0}, // +X -X -Z +Z
1207 raillike_group = nodedef->get(n).getGroup(raillike_groupname);
1212 bool sloped = false;
1213 for (int dir = 0; dir < 4; dir++) {
1214 bool rail_above = isSameRail(direction[dir] + v3s16(0, 1, 0));
1217 angle = slope_angle[dir];
1220 isSameRail(direction[dir]) ||
1221 isSameRail(direction[dir] + v3s16(0, -1, 0)))
1226 tile_index = straight;
1228 tile_index = rail_kinds[code].tile_index;
1229 angle = rail_kinds[code].angle;
1232 useTile(tile_index, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
1234 static const float offset = BS / 64;
1235 static const float size = BS / 2;
1236 float y2 = sloped ? size : -size;
1238 v3f(-size, y2 + offset, size),
1239 v3f( size, y2 + offset, size),
1240 v3f( size, -size + offset, -size),
1241 v3f(-size, -size + offset, -size),
1244 for (v3f &vertex : vertices)
1245 vertex.rotateXZBy(angle);
1249 void MapblockMeshGenerator::drawNodeboxNode()
1251 static const v3s16 tile_dirs[6] = {
1260 // we have this order for some reason...
1261 static const v3s16 connection_dirs[6] = {
1262 v3s16( 0, 1, 0), // top
1263 v3s16( 0, -1, 0), // bottom
1264 v3s16( 0, 0, -1), // front
1265 v3s16(-1, 0, 0), // left
1266 v3s16( 0, 0, 1), // back
1267 v3s16( 1, 0, 0), // right
1271 for (int face = 0; face < 6; face++) {
1272 // Handles facedir rotation for textures
1273 getTile(tile_dirs[face], &tiles[face]);
1276 // locate possible neighboring nodes to connect to
1277 int neighbors_set = 0;
1278 if (f->node_box.type == NODEBOX_CONNECTED) {
1279 for (int dir = 0; dir != 6; dir++) {
1280 int flag = 1 << dir;
1281 v3s16 p2 = blockpos_nodes + p + connection_dirs[dir];
1282 MapNode n2 = data->m_vmanip.getNodeNoEx(p2);
1283 if (nodedef->nodeboxConnects(n, n2, flag))
1284 neighbors_set |= flag;
1288 std::vector<aabb3f> boxes;
1289 n.getNodeBoxes(nodedef, &boxes, neighbors_set);
1290 for (const auto &box : boxes)
1291 drawAutoLightedCuboid(box, nullptr, tiles, 6);
1294 void MapblockMeshGenerator::drawMeshNode()
1298 bool private_mesh; // as a grab/drop pair is not thread-safe
1300 if (f->param_type_2 == CPT2_FACEDIR ||
1301 f->param_type_2 == CPT2_COLORED_FACEDIR) {
1302 facedir = n.getFaceDir(nodedef);
1303 } else if (f->param_type_2 == CPT2_WALLMOUNTED ||
1304 f->param_type_2 == CPT2_COLORED_WALLMOUNTED) {
1305 // Convert wallmounted to 6dfacedir.
1306 // When cache enabled, it is already converted.
1307 facedir = n.getWallMounted(nodedef);
1308 if (!enable_mesh_cache)
1309 facedir = wallmounted_to_facedir[facedir];
1312 if (!data->m_smooth_lighting && f->mesh_ptr[facedir]) {
1313 // use cached meshes
1314 private_mesh = false;
1315 mesh = f->mesh_ptr[facedir];
1316 } else if (f->mesh_ptr[0]) {
1317 // no cache, clone and rotate mesh
1318 private_mesh = true;
1319 mesh = cloneMesh(f->mesh_ptr[0]);
1320 rotateMeshBy6dFacedir(mesh, facedir);
1321 recalculateBoundingBox(mesh);
1322 meshmanip->recalculateNormals(mesh, true, false);
1326 int mesh_buffer_count = mesh->getMeshBufferCount();
1327 for (int j = 0; j < mesh_buffer_count; j++) {
1329 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1330 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1331 int vertex_count = buf->getVertexCount();
1333 if (data->m_smooth_lighting) {
1334 // Mesh is always private here. So the lighting is applied to each
1335 // vertex right here.
1336 for (int k = 0; k < vertex_count; k++) {
1337 video::S3DVertex &vertex = vertices[k];
1338 vertex.Color = blendLightColor(vertex.Pos, vertex.Normal);
1339 vertex.Pos += origin;
1341 collector->append(tile, vertices, vertex_count,
1342 buf->getIndices(), buf->getIndexCount());
1344 // Don't modify the mesh, it may not be private here.
1345 // Instead, let the collector process colors, etc.
1346 collector->append(tile, vertices, vertex_count,
1347 buf->getIndices(), buf->getIndexCount(), origin,
1348 color, f->light_source);
1355 // also called when the drawtype is known but should have been pre-converted
1356 void MapblockMeshGenerator::errorUnknownDrawtype()
1358 infostream << "Got drawtype " << f->drawtype << std::endl;
1359 FATAL_ERROR("Unknown drawtype");
1362 void MapblockMeshGenerator::drawNode()
1364 // skip some drawtypes early
1365 switch (f->drawtype) {
1366 case NDT_NORMAL: // Drawn by MapBlockMesh
1367 case NDT_AIRLIKE: // Not drawn at all
1368 case NDT_LIQUID: // Drawn by MapBlockMesh
1373 origin = intToFloat(p, BS);
1374 if (data->m_smooth_lighting)
1375 getSmoothLightFrame();
1377 light = LightPair(getInteriorLight(n, 1, nodedef));
1378 switch (f->drawtype) {
1379 case NDT_FLOWINGLIQUID: drawLiquidNode(); break;
1380 case NDT_GLASSLIKE: drawGlasslikeNode(); break;
1381 case NDT_GLASSLIKE_FRAMED: drawGlasslikeFramedNode(); break;
1382 case NDT_ALLFACES: drawAllfacesNode(); break;
1383 case NDT_TORCHLIKE: drawTorchlikeNode(); break;
1384 case NDT_SIGNLIKE: drawSignlikeNode(); break;
1385 case NDT_PLANTLIKE: drawPlantlikeNode(); break;
1386 case NDT_PLANTLIKE_ROOTED: drawPlantlikeRootedNode(); break;
1387 case NDT_FIRELIKE: drawFirelikeNode(); break;
1388 case NDT_FENCELIKE: drawFencelikeNode(); break;
1389 case NDT_RAILLIKE: drawRaillikeNode(); break;
1390 case NDT_NODEBOX: drawNodeboxNode(); break;
1391 case NDT_MESH: drawMeshNode(); break;
1392 default: errorUnknownDrawtype(); break;
1397 TODO: Fix alpha blending for special nodes
1398 Currently only the last element rendered is blended correct
1400 void MapblockMeshGenerator::generate()
1402 for (p.Z = 0; p.Z < MAP_BLOCKSIZE; p.Z++)
1403 for (p.Y = 0; p.Y < MAP_BLOCKSIZE; p.Y++)
1404 for (p.X = 0; p.X < MAP_BLOCKSIZE; p.X++) {
1405 n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1406 f = &nodedef->get(n);
1411 void MapblockMeshGenerator::renderSingle(content_t node)
1414 n = MapNode(node, 0xff, 0x00);
1415 f = &nodedef->get(n);