3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "content_mapblock.h"
21 #include "util/numeric.h"
22 #include "util/directiontables.h"
23 #include "mapblock_mesh.h"
26 #include "client/tile.h"
28 #include <IMeshManipulator.h>
29 #include "client/renderingengine.h"
33 // Distance of light extrapolation (for oversized nodes)
34 // After this distance, it gives up and considers light level constant
35 #define SMOOTH_LIGHTING_OVERSIZE 1.0
37 // Node edge count (for glasslike-framed)
38 #define FRAMED_EDGE_COUNT 12
40 // Node neighbor count, including edge-connected, but not vertex-connected
41 // (for glasslike-framed)
42 // Corresponding offsets are listed in g_27dirs
43 #define FRAMED_NEIGHBOR_COUNT 18
45 static const v3s16 light_dirs[8] = {
56 // Standard index set to make a quad on 4 vertices
57 static constexpr u16 quad_indices[] = {0, 1, 2, 2, 3, 0};
59 const std::string MapblockMeshGenerator::raillike_groupname = "connect_to_raillike";
61 MapblockMeshGenerator::MapblockMeshGenerator(MeshMakeData *input, MeshCollector *output)
66 nodedef = data->m_client->ndef();
67 meshmanip = RenderingEngine::get_scene_manager()->getMeshManipulator();
69 enable_mesh_cache = g_settings->getBool("enable_mesh_cache") &&
70 !data->m_smooth_lighting; // Mesh cache is not supported with smooth lighting
72 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
75 void MapblockMeshGenerator::useTile(int index, u8 set_flags, u8 reset_flags, bool special)
78 getSpecialTile(index, &tile, p == data->m_crack_pos_relative);
80 getTile(index, &tile);
81 if (!data->m_smooth_lighting)
82 color = encode_light(light, f->light_source);
84 for (auto &layer : tile.layers) {
85 layer.material_flags |= set_flags;
86 layer.material_flags &= ~reset_flags;
90 // Returns a tile, ready for use, non-rotated.
91 void MapblockMeshGenerator::getTile(int index, TileSpec *tile)
93 getNodeTileN(n, p, index, data, *tile);
96 // Returns a tile, ready for use, rotated according to the node facedir.
97 void MapblockMeshGenerator::getTile(v3s16 direction, TileSpec *tile)
99 getNodeTile(n, p, direction, data, *tile);
102 // Returns a special tile, ready for use, non-rotated.
103 void MapblockMeshGenerator::getSpecialTile(int index, TileSpec *tile, bool apply_crack)
105 *tile = f->special_tiles[index];
106 TileLayer *top_layer = nullptr;
108 for (auto &layernum : tile->layers) {
109 TileLayer *layer = &layernum;
110 if (layer->texture_id == 0)
113 if (!layer->has_color)
114 n.getColor(*f, &layer->color);
118 top_layer->material_flags |= MATERIAL_FLAG_CRACK;
121 void MapblockMeshGenerator::drawQuad(v3f *coords, const v3s16 &normal,
122 float vertical_tiling)
124 const v2f tcoords[4] = {v2f(0.0, 0.0), v2f(1.0, 0.0),
125 v2f(1.0, vertical_tiling), v2f(0.0, vertical_tiling)};
126 video::S3DVertex vertices[4];
127 bool shade_face = !f->light_source && (normal != v3s16(0, 0, 0));
128 v3f normal2(normal.X, normal.Y, normal.Z);
129 for (int j = 0; j < 4; j++) {
130 vertices[j].Pos = coords[j] + origin;
131 vertices[j].Normal = normal2;
132 if (data->m_smooth_lighting)
133 vertices[j].Color = blendLightColor(coords[j]);
135 vertices[j].Color = color;
137 applyFacesShading(vertices[j].Color, normal2);
138 vertices[j].TCoords = tcoords[j];
140 collector->append(tile, vertices, 4, quad_indices, 6);
144 // tiles - the tiles (materials) to use (for all 6 faces)
145 // tilecount - number of entries in tiles, 1<=tilecount<=6
146 // lights - vertex light levels. The order is the same as in light_dirs.
147 // NULL may be passed if smooth lighting is disabled.
148 // txc - texture coordinates - this is a list of texture coordinates
149 // for the opposite corners of each face - therefore, there
150 // should be (2+2)*6=24 values in the list. The order of
151 // the faces in the list is up-down-right-left-back-front
152 // (compatible with ContentFeatures).
153 void MapblockMeshGenerator::drawCuboid(const aabb3f &box,
154 TileSpec *tiles, int tilecount, const LightInfo *lights, const f32 *txc)
156 assert(tilecount >= 1 && tilecount <= 6); // pre-condition
158 v3f min = box.MinEdge;
159 v3f max = box.MaxEdge;
161 video::SColor colors[6];
162 if (!data->m_smooth_lighting) {
163 for (int face = 0; face != 6; ++face) {
164 colors[face] = encode_light(light, f->light_source);
166 if (!f->light_source) {
167 applyFacesShading(colors[0], v3f(0, 1, 0));
168 applyFacesShading(colors[1], v3f(0, -1, 0));
169 applyFacesShading(colors[2], v3f(1, 0, 0));
170 applyFacesShading(colors[3], v3f(-1, 0, 0));
171 applyFacesShading(colors[4], v3f(0, 0, 1));
172 applyFacesShading(colors[5], v3f(0, 0, -1));
176 video::S3DVertex vertices[24] = {
178 video::S3DVertex(min.X, max.Y, max.Z, 0, 1, 0, colors[0], txc[0], txc[1]),
179 video::S3DVertex(max.X, max.Y, max.Z, 0, 1, 0, colors[0], txc[2], txc[1]),
180 video::S3DVertex(max.X, max.Y, min.Z, 0, 1, 0, colors[0], txc[2], txc[3]),
181 video::S3DVertex(min.X, max.Y, min.Z, 0, 1, 0, colors[0], txc[0], txc[3]),
183 video::S3DVertex(min.X, min.Y, min.Z, 0, -1, 0, colors[1], txc[4], txc[5]),
184 video::S3DVertex(max.X, min.Y, min.Z, 0, -1, 0, colors[1], txc[6], txc[5]),
185 video::S3DVertex(max.X, min.Y, max.Z, 0, -1, 0, colors[1], txc[6], txc[7]),
186 video::S3DVertex(min.X, min.Y, max.Z, 0, -1, 0, colors[1], txc[4], txc[7]),
188 video::S3DVertex(max.X, max.Y, min.Z, 1, 0, 0, colors[2], txc[ 8], txc[9]),
189 video::S3DVertex(max.X, max.Y, max.Z, 1, 0, 0, colors[2], txc[10], txc[9]),
190 video::S3DVertex(max.X, min.Y, max.Z, 1, 0, 0, colors[2], txc[10], txc[11]),
191 video::S3DVertex(max.X, min.Y, min.Z, 1, 0, 0, colors[2], txc[ 8], txc[11]),
193 video::S3DVertex(min.X, max.Y, max.Z, -1, 0, 0, colors[3], txc[12], txc[13]),
194 video::S3DVertex(min.X, max.Y, min.Z, -1, 0, 0, colors[3], txc[14], txc[13]),
195 video::S3DVertex(min.X, min.Y, min.Z, -1, 0, 0, colors[3], txc[14], txc[15]),
196 video::S3DVertex(min.X, min.Y, max.Z, -1, 0, 0, colors[3], txc[12], txc[15]),
198 video::S3DVertex(max.X, max.Y, max.Z, 0, 0, 1, colors[4], txc[16], txc[17]),
199 video::S3DVertex(min.X, max.Y, max.Z, 0, 0, 1, colors[4], txc[18], txc[17]),
200 video::S3DVertex(min.X, min.Y, max.Z, 0, 0, 1, colors[4], txc[18], txc[19]),
201 video::S3DVertex(max.X, min.Y, max.Z, 0, 0, 1, colors[4], txc[16], txc[19]),
203 video::S3DVertex(min.X, max.Y, min.Z, 0, 0, -1, colors[5], txc[20], txc[21]),
204 video::S3DVertex(max.X, max.Y, min.Z, 0, 0, -1, colors[5], txc[22], txc[21]),
205 video::S3DVertex(max.X, min.Y, min.Z, 0, 0, -1, colors[5], txc[22], txc[23]),
206 video::S3DVertex(min.X, min.Y, min.Z, 0, 0, -1, colors[5], txc[20], txc[23]),
209 static const u8 light_indices[24] = {
218 for (int face = 0; face < 6; face++) {
219 int tileindex = MYMIN(face, tilecount - 1);
220 const TileSpec &tile = tiles[tileindex];
221 for (int j = 0; j < 4; j++) {
222 video::S3DVertex &vertex = vertices[face * 4 + j];
223 v2f &tcoords = vertex.TCoords;
224 switch (tile.rotation) {
228 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
231 tcoords.rotateBy(180, irr::core::vector2df(0, 0));
234 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
237 tcoords.X = 1.0 - tcoords.X;
238 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
241 tcoords.X = 1.0 - tcoords.X;
242 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
245 tcoords.Y = 1.0 - tcoords.Y;
246 tcoords.rotateBy(90, irr::core::vector2df(0, 0));
249 tcoords.Y = 1.0 - tcoords.Y;
250 tcoords.rotateBy(270, irr::core::vector2df(0, 0));
253 tcoords.X = 1.0 - tcoords.X;
256 tcoords.Y = 1.0 - tcoords.Y;
264 if (data->m_smooth_lighting) {
265 for (int j = 0; j < 24; ++j) {
266 video::S3DVertex &vertex = vertices[j];
267 vertex.Color = encode_light(
268 lights[light_indices[j]].getPair(MYMAX(0.0f, vertex.Normal.Y)),
270 if (!f->light_source)
271 applyFacesShading(vertex.Color, vertex.Normal);
275 // Add to mesh collector
276 for (int k = 0; k < 6; ++k) {
277 int tileindex = MYMIN(k, tilecount - 1);
278 collector->append(tiles[tileindex], vertices + 4 * k, 4, quad_indices, 6);
282 // Gets the base lighting values for a node
283 void MapblockMeshGenerator::getSmoothLightFrame()
285 for (int k = 0; k < 8; ++k)
286 frame.sunlight[k] = false;
287 for (int k = 0; k < 8; ++k) {
288 LightPair light(getSmoothLightTransparent(blockpos_nodes + p, light_dirs[k], data));
289 frame.lightsDay[k] = light.lightDay;
290 frame.lightsNight[k] = light.lightNight;
291 // If there is direct sunlight and no ambient occlusion at some corner,
292 // mark the vertical edge (top and bottom corners) containing it.
293 if (light.lightDay == 255) {
294 frame.sunlight[k] = true;
295 frame.sunlight[k ^ 2] = true;
300 // Calculates vertex light level
301 // vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
302 LightInfo MapblockMeshGenerator::blendLight(const v3f &vertex_pos)
304 // Light levels at (logical) node corners are known. Here,
305 // trilinear interpolation is used to calculate light level
306 // at a given point in the node.
307 f32 x = core::clamp(vertex_pos.X / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
308 f32 y = core::clamp(vertex_pos.Y / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
309 f32 z = core::clamp(vertex_pos.Z / BS + 0.5, 0.0 - SMOOTH_LIGHTING_OVERSIZE, 1.0 + SMOOTH_LIGHTING_OVERSIZE);
310 f32 lightDay = 0.0; // daylight
311 f32 lightNight = 0.0;
312 f32 lightBoosted = 0.0; // daylight + direct sunlight, if any
313 for (int k = 0; k < 8; ++k) {
314 f32 dx = (k & 4) ? x : 1 - x;
315 f32 dy = (k & 2) ? y : 1 - y;
316 f32 dz = (k & 1) ? z : 1 - z;
317 // Use direct sunlight (255), if any; use daylight otherwise.
318 f32 light_boosted = frame.sunlight[k] ? 255 : frame.lightsDay[k];
319 lightDay += dx * dy * dz * frame.lightsDay[k];
320 lightNight += dx * dy * dz * frame.lightsNight[k];
321 lightBoosted += dx * dy * dz * light_boosted;
323 return LightInfo{lightDay, lightNight, lightBoosted};
326 // Calculates vertex color to be used in mapblock mesh
327 // vertex_pos - vertex position in the node (coordinates are clamped to [0.0, 1.0] or so)
328 // tile_color - node's tile color
329 video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos)
331 LightInfo light = blendLight(vertex_pos);
332 return encode_light(light.getPair(), f->light_source);
335 video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos,
336 const v3f &vertex_normal)
338 LightInfo light = blendLight(vertex_pos);
339 video::SColor color = encode_light(light.getPair(MYMAX(0.0f, vertex_normal.Y)), f->light_source);
340 if (!f->light_source)
341 applyFacesShading(color, vertex_normal);
345 void MapblockMeshGenerator::generateCuboidTextureCoords(const aabb3f &box, f32 *coords)
347 f32 tx1 = (box.MinEdge.X / BS) + 0.5;
348 f32 ty1 = (box.MinEdge.Y / BS) + 0.5;
349 f32 tz1 = (box.MinEdge.Z / BS) + 0.5;
350 f32 tx2 = (box.MaxEdge.X / BS) + 0.5;
351 f32 ty2 = (box.MaxEdge.Y / BS) + 0.5;
352 f32 tz2 = (box.MaxEdge.Z / BS) + 0.5;
354 tx1, 1 - tz2, tx2, 1 - tz1, // up
355 tx1, tz1, tx2, tz2, // down
356 tz1, 1 - ty2, tz2, 1 - ty1, // right
357 1 - tz2, 1 - ty2, 1 - tz1, 1 - ty1, // left
358 1 - tx2, 1 - ty2, 1 - tx1, 1 - ty1, // back
359 tx1, 1 - ty2, tx2, 1 - ty1, // front
361 for (int i = 0; i != 24; ++i)
365 void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc,
366 TileSpec *tiles, int tile_count)
368 f32 texture_coord_buf[24];
369 f32 dx1 = box.MinEdge.X;
370 f32 dy1 = box.MinEdge.Y;
371 f32 dz1 = box.MinEdge.Z;
372 f32 dx2 = box.MaxEdge.X;
373 f32 dy2 = box.MaxEdge.Y;
374 f32 dz2 = box.MaxEdge.Z;
375 box.MinEdge += origin;
376 box.MaxEdge += origin;
378 generateCuboidTextureCoords(box, texture_coord_buf);
379 txc = texture_coord_buf;
385 if (data->m_smooth_lighting) {
387 for (int j = 0; j < 8; ++j) {
389 d.X = (j & 4) ? dx2 : dx1;
390 d.Y = (j & 2) ? dy2 : dy1;
391 d.Z = (j & 1) ? dz2 : dz1;
392 lights[j] = blendLight(d);
394 drawCuboid(box, tiles, tile_count, lights, txc);
396 drawCuboid(box, tiles, tile_count, nullptr, txc);
400 void MapblockMeshGenerator::prepareLiquidNodeDrawing()
402 getSpecialTile(0, &tile_liquid_top);
403 getSpecialTile(1, &tile_liquid);
405 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y + 1, p.Z));
406 MapNode nbottom = data->m_vmanip.getNodeNoEx(blockpos_nodes + v3s16(p.X, p.Y - 1, p.Z));
407 c_flowing = nodedef->getId(f->liquid_alternative_flowing);
408 c_source = nodedef->getId(f->liquid_alternative_source);
409 top_is_same_liquid = (ntop.getContent() == c_flowing) || (ntop.getContent() == c_source);
410 draw_liquid_bottom = (nbottom.getContent() != c_flowing) && (nbottom.getContent() != c_source);
411 if (draw_liquid_bottom) {
412 const ContentFeatures &f2 = nodedef->get(nbottom.getContent());
413 if (f2.solidness > 1)
414 draw_liquid_bottom = false;
417 if (data->m_smooth_lighting)
418 return; // don't need to pre-compute anything in this case
420 if (f->light_source != 0) {
421 // If this liquid emits light and doesn't contain light, draw
422 // it at what it emits, for an increased effect
423 u8 e = decode_light(f->light_source);
424 light = LightPair(std::max(e, light.lightDay), std::max(e, light.lightNight));
425 } else if (nodedef->get(ntop).param_type == CPT_LIGHT) {
426 // Otherwise, use the light of the node on top if possible
427 light = LightPair(getInteriorLight(ntop, 0, nodedef));
430 color_liquid_top = encode_light(light, f->light_source);
431 color = encode_light(light, f->light_source);
434 void MapblockMeshGenerator::getLiquidNeighborhood()
436 u8 range = rangelim(nodedef->get(c_flowing).liquid_range, 1, 8);
438 for (int w = -1; w <= 1; w++)
439 for (int u = -1; u <= 1; u++) {
440 NeighborData &neighbor = liquid_neighbors[w + 1][u + 1];
441 v3s16 p2 = p + v3s16(u, 0, w);
442 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
443 neighbor.content = n2.getContent();
444 neighbor.level = -0.5 * BS;
445 neighbor.is_same_liquid = false;
446 neighbor.top_is_same_liquid = false;
448 if (neighbor.content == CONTENT_IGNORE)
451 if (neighbor.content == c_source) {
452 neighbor.is_same_liquid = true;
453 neighbor.level = 0.5 * BS;
454 } else if (neighbor.content == c_flowing) {
455 neighbor.is_same_liquid = true;
456 u8 liquid_level = (n2.param2 & LIQUID_LEVEL_MASK);
457 if (liquid_level <= LIQUID_LEVEL_MAX + 1 - range)
460 liquid_level -= (LIQUID_LEVEL_MAX + 1 - range);
461 neighbor.level = (-0.5 + (liquid_level + 0.5) / range) * BS;
464 // Check node above neighbor.
465 // NOTE: This doesn't get executed if neighbor
468 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
469 if (n2.getContent() == c_source || n2.getContent() == c_flowing)
470 neighbor.top_is_same_liquid = true;
474 void MapblockMeshGenerator::calculateCornerLevels()
476 for (int k = 0; k < 2; k++)
477 for (int i = 0; i < 2; i++)
478 corner_levels[k][i] = getCornerLevel(i, k);
481 f32 MapblockMeshGenerator::getCornerLevel(int i, int k)
486 for (int dk = 0; dk < 2; dk++)
487 for (int di = 0; di < 2; di++) {
488 NeighborData &neighbor_data = liquid_neighbors[k + dk][i + di];
489 content_t content = neighbor_data.content;
491 // If top is liquid, draw starting from top of node
492 if (neighbor_data.top_is_same_liquid)
495 // Source always has the full height
496 if (content == c_source)
499 // Flowing liquid has level information
500 if (content == c_flowing) {
501 sum += neighbor_data.level;
503 } else if (content == CONTENT_AIR) {
506 return -0.5 * BS + 0.2;
514 void MapblockMeshGenerator::drawLiquidSides()
516 struct LiquidFaceDesc {
518 v3s16 p[2]; // XZ only; 1 means +, 0 means -
523 static const LiquidFaceDesc base_faces[4] = {
524 {v3s16( 1, 0, 0), {v3s16(1, 0, 1), v3s16(1, 0, 0)}},
525 {v3s16(-1, 0, 0), {v3s16(0, 0, 0), v3s16(0, 0, 1)}},
526 {v3s16( 0, 0, 1), {v3s16(0, 0, 1), v3s16(1, 0, 1)}},
527 {v3s16( 0, 0, -1), {v3s16(1, 0, 0), v3s16(0, 0, 0)}},
529 static const UV base_vertices[4] = {
536 for (const auto &face : base_faces) {
537 const NeighborData &neighbor = liquid_neighbors[face.dir.Z + 1][face.dir.X + 1];
539 // No face between nodes of the same liquid, unless there is node
540 // at the top to which it should be connected. Again, unless the face
541 // there would be inside the liquid
542 if (neighbor.is_same_liquid) {
543 if (!top_is_same_liquid)
545 if (neighbor.top_is_same_liquid)
549 const ContentFeatures &neighbor_features = nodedef->get(neighbor.content);
550 // Don't draw face if neighbor is blocking the view
551 if (neighbor_features.solidness == 2)
554 video::S3DVertex vertices[4];
555 for (int j = 0; j < 4; j++) {
556 const UV &vertex = base_vertices[j];
557 const v3s16 &base = face.p[vertex.u];
559 pos.X = (base.X - 0.5) * BS;
560 pos.Z = (base.Z - 0.5) * BS;
562 pos.Y = neighbor.is_same_liquid ? corner_levels[base.Z][base.X] : -0.5 * BS;
564 pos.Y = !top_is_same_liquid ? corner_levels[base.Z][base.X] : 0.5 * BS;
565 if (data->m_smooth_lighting)
566 color = blendLightColor(pos);
568 vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, color, vertex.u, vertex.v);
570 collector->append(tile_liquid, vertices, 4, quad_indices, 6);
574 void MapblockMeshGenerator::drawLiquidTop()
576 // To get backface culling right, the vertices need to go
577 // clockwise around the front of the face. And we happened to
578 // calculate corner levels in exact reverse order.
579 static const int corner_resolve[4][2] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
581 video::S3DVertex vertices[4] = {
582 video::S3DVertex(-BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 0, 1),
583 video::S3DVertex( BS / 2, 0, BS / 2, 0, 0, 0, color_liquid_top, 1, 1),
584 video::S3DVertex( BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 1, 0),
585 video::S3DVertex(-BS / 2, 0, -BS / 2, 0, 0, 0, color_liquid_top, 0, 0),
588 for (int i = 0; i < 4; i++) {
589 int u = corner_resolve[i][0];
590 int w = corner_resolve[i][1];
591 vertices[i].Pos.Y += corner_levels[w][u];
592 if (data->m_smooth_lighting)
593 vertices[i].Color = blendLightColor(vertices[i].Pos);
594 vertices[i].Pos += origin;
597 // Default downwards-flowing texture animation goes from
598 // -Z towards +Z, thus the direction is +Z.
599 // Rotate texture to make animation go in flow direction
600 // Positive if liquid moves towards +Z
601 f32 dz = (corner_levels[0][0] + corner_levels[0][1]) -
602 (corner_levels[1][0] + corner_levels[1][1]);
603 // Positive if liquid moves towards +X
604 f32 dx = (corner_levels[0][0] + corner_levels[1][0]) -
605 (corner_levels[0][1] + corner_levels[1][1]);
606 f32 tcoord_angle = atan2(dz, dx) * core::RADTODEG;
607 v2f tcoord_center(0.5, 0.5);
608 v2f tcoord_translate(blockpos_nodes.Z + p.Z, blockpos_nodes.X + p.X);
609 tcoord_translate.rotateBy(tcoord_angle);
610 tcoord_translate.X -= floor(tcoord_translate.X);
611 tcoord_translate.Y -= floor(tcoord_translate.Y);
613 for (video::S3DVertex &vertex : vertices) {
614 vertex.TCoords.rotateBy(tcoord_angle, tcoord_center);
615 vertex.TCoords += tcoord_translate;
618 std::swap(vertices[0].TCoords, vertices[2].TCoords);
620 collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
623 void MapblockMeshGenerator::drawLiquidBottom()
625 video::S3DVertex vertices[4] = {
626 video::S3DVertex(-BS / 2, -BS / 2, -BS / 2, 0, 0, 0, color_liquid_top, 0, 0),
627 video::S3DVertex( BS / 2, -BS / 2, -BS / 2, 0, 0, 0, color_liquid_top, 1, 0),
628 video::S3DVertex( BS / 2, -BS / 2, BS / 2, 0, 0, 0, color_liquid_top, 1, 1),
629 video::S3DVertex(-BS / 2, -BS / 2, BS / 2, 0, 0, 0, color_liquid_top, 0, 1),
632 for (int i = 0; i < 4; i++) {
633 if (data->m_smooth_lighting)
634 vertices[i].Color = blendLightColor(vertices[i].Pos);
635 vertices[i].Pos += origin;
638 collector->append(tile_liquid_top, vertices, 4, quad_indices, 6);
641 void MapblockMeshGenerator::drawLiquidNode()
643 prepareLiquidNodeDrawing();
644 getLiquidNeighborhood();
645 calculateCornerLevels();
647 if (!top_is_same_liquid)
649 if (draw_liquid_bottom)
653 void MapblockMeshGenerator::drawGlasslikeNode()
657 for (int face = 0; face < 6; face++) {
658 // Check this neighbor
659 v3s16 dir = g_6dirs[face];
660 v3s16 neighbor_pos = blockpos_nodes + p + dir;
661 MapNode neighbor = data->m_vmanip.getNodeNoExNoEmerge(neighbor_pos);
662 // Don't make face if neighbor is of same type
663 if (neighbor.getContent() == n.getContent())
667 v3f(-BS / 2, BS / 2, -BS / 2),
668 v3f( BS / 2, BS / 2, -BS / 2),
669 v3f( BS / 2, -BS / 2, -BS / 2),
670 v3f(-BS / 2, -BS / 2, -BS / 2),
673 for (v3f &vertex : vertices) {
676 vertex.rotateXZBy(180); break;
678 vertex.rotateYZBy( 90); break;
680 vertex.rotateXZBy( 90); break;
682 vertex.rotateXZBy( 0); break;
684 vertex.rotateYZBy(-90); break;
686 vertex.rotateXZBy(-90); break;
689 drawQuad(vertices, dir);
693 void MapblockMeshGenerator::drawGlasslikeFramedNode()
696 for (int face = 0; face < 6; face++)
697 getTile(g_6dirs[face], &tiles[face]);
699 TileSpec glass_tiles[6];
700 if (tiles[1].layers[0].texture &&
701 tiles[2].layers[0].texture &&
702 tiles[3].layers[0].texture) {
703 glass_tiles[0] = tiles[4];
704 glass_tiles[1] = tiles[0];
705 glass_tiles[2] = tiles[4];
706 glass_tiles[3] = tiles[4];
707 glass_tiles[4] = tiles[3];
708 glass_tiles[5] = tiles[4];
710 for (auto &glass_tile : glass_tiles)
711 glass_tile = tiles[4];
714 u8 param2 = n.getParam2();
715 bool H_merge = !(param2 & 128);
716 bool V_merge = !(param2 & 64);
719 static const float a = BS / 2;
720 static const float g = a - 0.003;
721 static const float b = .876 * ( BS / 2 );
723 static const aabb3f frame_edges[FRAMED_EDGE_COUNT] = {
724 aabb3f( b, b, -a, a, a, a), // y+
725 aabb3f(-a, b, -a, -b, a, a), // y+
726 aabb3f( b, -a, -a, a, -b, a), // y-
727 aabb3f(-a, -a, -a, -b, -b, a), // y-
728 aabb3f( b, -a, b, a, a, a), // x+
729 aabb3f( b, -a, -a, a, a, -b), // x+
730 aabb3f(-a, -a, b, -b, a, a), // x-
731 aabb3f(-a, -a, -a, -b, a, -b), // x-
732 aabb3f(-a, b, b, a, a, a), // z+
733 aabb3f(-a, -a, b, a, -b, a), // z+
734 aabb3f(-a, -a, -a, a, -b, -b), // z-
735 aabb3f(-a, b, -a, a, a, -b), // z-
737 static const aabb3f glass_faces[6] = {
738 aabb3f(-g, -g, g, g, g, g), // z+
739 aabb3f(-g, g, -g, g, g, g), // y+
740 aabb3f( g, -g, -g, g, g, g), // x+
741 aabb3f(-g, -g, -g, g, g, -g), // z-
742 aabb3f(-g, -g, -g, g, -g, g), // y-
743 aabb3f(-g, -g, -g, -g, g, g), // x-
746 // tables of neighbour (connect if same type and merge allowed),
747 // checked with g_26dirs
749 // 1 = connect, 0 = face visible
750 bool nb[FRAMED_NEIGHBOR_COUNT] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
753 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};
754 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};
755 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};
756 const bool *check_nb = check_nb_all;
758 // neighbours checks for frames visibility
759 if (H_merge || V_merge) {
761 check_nb = check_nb_vertical; // vertical-only merge
763 check_nb = check_nb_horizontal; // horizontal-only merge
764 content_t current = n.getContent();
765 for (int i = 0; i < FRAMED_NEIGHBOR_COUNT; i++) {
768 v3s16 n2p = blockpos_nodes + p + g_26dirs[i];
769 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
770 content_t n2c = n2.getContent();
778 static const u8 nb_triplet[FRAMED_EDGE_COUNT][3] = {
779 {1, 2, 7}, {1, 5, 6}, {4, 2, 15}, {4, 5, 14},
780 {2, 0, 11}, {2, 3, 13}, {5, 0, 10}, {5, 3, 12},
781 {0, 1, 8}, {0, 4, 16}, {3, 4, 17}, {3, 1, 9},
785 for (int edge = 0; edge < FRAMED_EDGE_COUNT; edge++) {
787 if (nb[nb_triplet[edge][2]])
788 edge_invisible = nb[nb_triplet[edge][0]] & nb[nb_triplet[edge][1]];
790 edge_invisible = nb[nb_triplet[edge][0]] ^ nb[nb_triplet[edge][1]];
793 drawAutoLightedCuboid(frame_edges[edge]);
796 for (int face = 0; face < 6; face++) {
799 tile = glass_tiles[face];
800 drawAutoLightedCuboid(glass_faces[face]);
803 // Optionally render internal liquid level defined by param2
804 // Liquid is textured with 1 tile defined in nodedef 'special_tiles'
805 if (param2 > 0 && f->param_type_2 == CPT2_GLASSLIKE_LIQUID_LEVEL &&
806 f->special_tiles[0].layers[0].texture) {
807 // Internal liquid level has param2 range 0 .. 63,
808 // convert it to -0.5 .. 0.5
809 float vlev = (param2 / 63.0) * 2.0 - 1.0;
810 getSpecialTile(0, &tile);
811 drawAutoLightedCuboid(aabb3f(-(nb[5] ? g : b),
815 (nb[1] ? g : b) * vlev,
820 void MapblockMeshGenerator::drawAllfacesNode()
822 static const aabb3f box(-BS / 2, -BS / 2, -BS / 2, BS / 2, BS / 2, BS / 2);
824 drawAutoLightedCuboid(box);
827 void MapblockMeshGenerator::drawTorchlikeNode()
829 u8 wall = n.getWallMounted(nodedef);
832 case DWM_YP: tileindex = 1; break; // ceiling
833 case DWM_YN: tileindex = 0; break; // floor
834 default: tileindex = 2; // side (or invalid—should we care?)
836 useTile(tileindex, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
838 float size = BS / 2 * f->visual_scale;
842 v3f( size, -size, 0),
843 v3f(-size, -size, 0),
846 for (v3f &vertex : vertices) {
849 vertex.rotateXZBy(-45); break;
851 vertex.rotateXZBy( 45); break;
853 vertex.rotateXZBy( 0); break;
855 vertex.rotateXZBy(180); break;
857 vertex.rotateXZBy( 90); break;
859 vertex.rotateXZBy(-90); break;
865 void MapblockMeshGenerator::drawSignlikeNode()
867 u8 wall = n.getWallMounted(nodedef);
868 useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
869 static const float offset = BS / 16;
870 float size = BS / 2 * f->visual_scale;
871 // Wall at X+ of node
873 v3f(BS / 2 - offset, size, size),
874 v3f(BS / 2 - offset, size, -size),
875 v3f(BS / 2 - offset, -size, -size),
876 v3f(BS / 2 - offset, -size, size),
879 for (v3f &vertex : vertices) {
882 vertex.rotateXYBy( 90); break;
884 vertex.rotateXYBy(-90); break;
886 vertex.rotateXZBy( 0); break;
888 vertex.rotateXZBy(180); break;
890 vertex.rotateXZBy( 90); break;
892 vertex.rotateXZBy(-90); break;
898 void MapblockMeshGenerator::drawPlantlikeQuad(float rotation, float quad_offset,
899 bool offset_top_only)
902 v3f(-scale, -BS / 2 + 2.0 * scale * plant_height, 0),
903 v3f( scale, -BS / 2 + 2.0 * scale * plant_height, 0),
904 v3f( scale, -BS / 2, 0),
905 v3f(-scale, -BS / 2, 0),
907 if (random_offset_Y) {
908 PseudoRandom yrng(face_num++ | p.X << 16 | p.Z << 8 | p.Y << 24);
909 offset.Y = -BS * ((yrng.next() % 16 / 16.0) * 0.125);
911 int offset_count = offset_top_only ? 2 : 4;
912 for (int i = 0; i < offset_count; i++)
913 vertices[i].Z += quad_offset;
915 for (v3f &vertex : vertices) {
916 vertex.rotateXZBy(rotation + rotate_degree);
919 drawQuad(vertices, v3s16(0, 0, 0), plant_height);
922 void MapblockMeshGenerator::drawPlantlike()
924 draw_style = PLANT_STYLE_CROSS;
925 scale = BS / 2 * f->visual_scale;
926 offset = v3f(0, 0, 0);
928 random_offset_Y = false;
932 switch (f->param_type_2) {
933 case CPT2_MESHOPTIONS:
934 draw_style = PlantlikeStyle(n.param2 & MO_MASK_STYLE);
935 if (n.param2 & MO_BIT_SCALE_SQRT2)
937 if (n.param2 & MO_BIT_RANDOM_OFFSET) {
938 PseudoRandom rng(p.X << 8 | p.Z | p.Y << 16);
939 offset.X = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
940 offset.Z = BS * ((rng.next() % 16 / 16.0) * 0.29 - 0.145);
942 if (n.param2 & MO_BIT_RANDOM_OFFSET_Y)
943 random_offset_Y = true;
947 rotate_degree = n.param2 * 2;
951 plant_height = n.param2 / 16.0;
958 switch (draw_style) {
959 case PLANT_STYLE_CROSS:
960 drawPlantlikeQuad(46);
961 drawPlantlikeQuad(-44);
964 case PLANT_STYLE_CROSS2:
965 drawPlantlikeQuad(91);
966 drawPlantlikeQuad(1);
969 case PLANT_STYLE_STAR:
970 drawPlantlikeQuad(121);
971 drawPlantlikeQuad(241);
972 drawPlantlikeQuad(1);
975 case PLANT_STYLE_HASH:
976 drawPlantlikeQuad( 1, BS / 4);
977 drawPlantlikeQuad( 91, BS / 4);
978 drawPlantlikeQuad(181, BS / 4);
979 drawPlantlikeQuad(271, BS / 4);
982 case PLANT_STYLE_HASH2:
983 drawPlantlikeQuad( 1, -BS / 2, true);
984 drawPlantlikeQuad( 91, -BS / 2, true);
985 drawPlantlikeQuad(181, -BS / 2, true);
986 drawPlantlikeQuad(271, -BS / 2, true);
991 void MapblockMeshGenerator::drawPlantlikeNode()
997 void MapblockMeshGenerator::drawPlantlikeRootedNode()
999 useTile(0, MATERIAL_FLAG_CRACK_OVERLAY, 0, true);
1000 origin += v3f(0.0, BS, 0.0);
1002 if (data->m_smooth_lighting) {
1003 getSmoothLightFrame();
1005 MapNode ntop = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1006 light = LightPair(getInteriorLight(ntop, 1, nodedef));
1012 void MapblockMeshGenerator::drawFirelikeQuad(float rotation, float opening_angle,
1013 float offset_h, float offset_v)
1016 v3f(-scale, -BS / 2 + scale * 2, 0),
1017 v3f( scale, -BS / 2 + scale * 2, 0),
1018 v3f( scale, -BS / 2, 0),
1019 v3f(-scale, -BS / 2, 0),
1022 for (v3f &vertex : vertices) {
1023 vertex.rotateYZBy(opening_angle);
1024 vertex.Z += offset_h;
1025 vertex.rotateXZBy(rotation);
1026 vertex.Y += offset_v;
1031 void MapblockMeshGenerator::drawFirelikeNode()
1034 scale = BS / 2 * f->visual_scale;
1036 // Check for adjacent nodes
1037 bool neighbors = false;
1038 bool neighbor[6] = {0, 0, 0, 0, 0, 0};
1039 content_t current = n.getContent();
1040 for (int i = 0; i < 6; i++) {
1041 v3s16 n2p = blockpos_nodes + p + g_6dirs[i];
1042 MapNode n2 = data->m_vmanip.getNodeNoEx(n2p);
1043 content_t n2c = n2.getContent();
1044 if (n2c != CONTENT_IGNORE && n2c != CONTENT_AIR && n2c != current) {
1049 bool drawBasicFire = neighbor[D6D_YN] || !neighbors;
1050 bool drawBottomFire = neighbor[D6D_YP];
1052 if (drawBasicFire || neighbor[D6D_ZP])
1053 drawFirelikeQuad(0, -10, 0.4 * BS);
1054 else if (drawBottomFire)
1055 drawFirelikeQuad(0, 70, 0.47 * BS, 0.484 * BS);
1057 if (drawBasicFire || neighbor[D6D_XN])
1058 drawFirelikeQuad(90, -10, 0.4 * BS);
1059 else if (drawBottomFire)
1060 drawFirelikeQuad(90, 70, 0.47 * BS, 0.484 * BS);
1062 if (drawBasicFire || neighbor[D6D_ZN])
1063 drawFirelikeQuad(180, -10, 0.4 * BS);
1064 else if (drawBottomFire)
1065 drawFirelikeQuad(180, 70, 0.47 * BS, 0.484 * BS);
1067 if (drawBasicFire || neighbor[D6D_XP])
1068 drawFirelikeQuad(270, -10, 0.4 * BS);
1069 else if (drawBottomFire)
1070 drawFirelikeQuad(270, 70, 0.47 * BS, 0.484 * BS);
1072 if (drawBasicFire) {
1073 drawFirelikeQuad(45, 0, 0.0);
1074 drawFirelikeQuad(-45, 0, 0.0);
1078 void MapblockMeshGenerator::drawFencelikeNode()
1081 TileSpec tile_nocrack = tile;
1083 for (auto &layer : tile_nocrack.layers)
1084 layer.material_flags &= ~MATERIAL_FLAG_CRACK;
1086 // Put wood the right way around in the posts
1087 TileSpec tile_rot = tile;
1088 tile_rot.rotation = 1;
1090 static const f32 post_rad = BS / 8;
1091 static const f32 bar_rad = BS / 16;
1092 static const f32 bar_len = BS / 2 - post_rad;
1094 // The post - always present
1095 static const aabb3f post(-post_rad, -BS / 2, -post_rad,
1096 post_rad, BS / 2, post_rad);
1097 static const f32 postuv[24] = {
1098 0.375, 0.375, 0.625, 0.625,
1099 0.375, 0.375, 0.625, 0.625,
1100 0.000, 0.000, 0.250, 1.000,
1101 0.250, 0.000, 0.500, 1.000,
1102 0.500, 0.000, 0.750, 1.000,
1103 0.750, 0.000, 1.000, 1.000,
1106 drawAutoLightedCuboid(post, postuv);
1108 tile = tile_nocrack;
1110 // Now a section of fence, +X, if there's a post there
1113 MapNode n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1114 const ContentFeatures *f2 = &nodedef->get(n2);
1115 if (f2->drawtype == NDT_FENCELIKE) {
1116 static const aabb3f bar_x1(BS / 2 - bar_len, BS / 4 - bar_rad, -bar_rad,
1117 BS / 2 + bar_len, BS / 4 + bar_rad, bar_rad);
1118 static const aabb3f bar_x2(BS / 2 - bar_len, -BS / 4 - bar_rad, -bar_rad,
1119 BS / 2 + bar_len, -BS / 4 + bar_rad, bar_rad);
1120 static const f32 xrailuv[24] = {
1121 0.000, 0.125, 1.000, 0.250,
1122 0.000, 0.250, 1.000, 0.375,
1123 0.375, 0.375, 0.500, 0.500,
1124 0.625, 0.625, 0.750, 0.750,
1125 0.000, 0.500, 1.000, 0.625,
1126 0.000, 0.875, 1.000, 1.000,
1128 drawAutoLightedCuboid(bar_x1, xrailuv);
1129 drawAutoLightedCuboid(bar_x2, xrailuv);
1132 // Now a section of fence, +Z, if there's a post there
1135 n2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p2);
1136 f2 = &nodedef->get(n2);
1137 if (f2->drawtype == NDT_FENCELIKE) {
1138 static const aabb3f bar_z1(-bar_rad, BS / 4 - bar_rad, BS / 2 - bar_len,
1139 bar_rad, BS / 4 + bar_rad, BS / 2 + bar_len);
1140 static const aabb3f bar_z2(-bar_rad, -BS / 4 - bar_rad, BS / 2 - bar_len,
1141 bar_rad, -BS / 4 + bar_rad, BS / 2 + bar_len);
1142 static const f32 zrailuv[24] = {
1143 0.1875, 0.0625, 0.3125, 0.3125, // cannot rotate; stretch
1144 0.2500, 0.0625, 0.3750, 0.3125, // for wood texture instead
1145 0.0000, 0.5625, 1.0000, 0.6875,
1146 0.0000, 0.3750, 1.0000, 0.5000,
1147 0.3750, 0.3750, 0.5000, 0.5000,
1148 0.6250, 0.6250, 0.7500, 0.7500,
1150 drawAutoLightedCuboid(bar_z1, zrailuv);
1151 drawAutoLightedCuboid(bar_z2, zrailuv);
1155 bool MapblockMeshGenerator::isSameRail(v3s16 dir)
1157 MapNode node2 = data->m_vmanip.getNodeNoEx(blockpos_nodes + p + dir);
1158 if (node2.getContent() == n.getContent())
1160 const ContentFeatures &def2 = nodedef->get(node2);
1161 return ((def2.drawtype == NDT_RAILLIKE) &&
1162 (def2.getGroup(raillike_groupname) == raillike_group));
1165 void MapblockMeshGenerator::drawRaillikeNode()
1167 static const v3s16 direction[4] = {
1173 static const int slope_angle[4] = {0, 180, 90, -90};
1185 static const RailDesc rail_kinds[16] = {
1188 {straight, 0}, // . . . .
1189 {straight, 0}, // . . . +Z
1190 {straight, 0}, // . . -Z .
1191 {straight, 0}, // . . -Z +Z
1192 {straight, 90}, // . -X . .
1193 { curved, 180}, // . -X . +Z
1194 { curved, 270}, // . -X -Z .
1195 {junction, 180}, // . -X -Z +Z
1196 {straight, 90}, // +X . . .
1197 { curved, 90}, // +X . . +Z
1198 { curved, 0}, // +X . -Z .
1199 {junction, 0}, // +X . -Z +Z
1200 {straight, 90}, // +X -X . .
1201 {junction, 90}, // +X -X . +Z
1202 {junction, 270}, // +X -X -Z .
1203 { cross, 0}, // +X -X -Z +Z
1206 raillike_group = nodedef->get(n).getGroup(raillike_groupname);
1211 bool sloped = false;
1212 for (int dir = 0; dir < 4; dir++) {
1213 bool rail_above = isSameRail(direction[dir] + v3s16(0, 1, 0));
1216 angle = slope_angle[dir];
1219 isSameRail(direction[dir]) ||
1220 isSameRail(direction[dir] + v3s16(0, -1, 0)))
1225 tile_index = straight;
1227 tile_index = rail_kinds[code].tile_index;
1228 angle = rail_kinds[code].angle;
1231 useTile(tile_index, MATERIAL_FLAG_CRACK_OVERLAY, MATERIAL_FLAG_BACKFACE_CULLING);
1233 static const float offset = BS / 64;
1234 static const float size = BS / 2;
1235 float y2 = sloped ? size : -size;
1237 v3f(-size, y2 + offset, size),
1238 v3f( size, y2 + offset, size),
1239 v3f( size, -size + offset, -size),
1240 v3f(-size, -size + offset, -size),
1243 for (v3f &vertex : vertices)
1244 vertex.rotateXZBy(angle);
1248 void MapblockMeshGenerator::drawNodeboxNode()
1250 static const v3s16 tile_dirs[6] = {
1259 // we have this order for some reason...
1260 static const v3s16 connection_dirs[6] = {
1261 v3s16( 0, 1, 0), // top
1262 v3s16( 0, -1, 0), // bottom
1263 v3s16( 0, 0, -1), // front
1264 v3s16(-1, 0, 0), // left
1265 v3s16( 0, 0, 1), // back
1266 v3s16( 1, 0, 0), // right
1270 for (int face = 0; face < 6; face++) {
1271 // Handles facedir rotation for textures
1272 getTile(tile_dirs[face], &tiles[face]);
1275 // locate possible neighboring nodes to connect to
1276 int neighbors_set = 0;
1277 if (f->node_box.type == NODEBOX_CONNECTED) {
1278 for (int dir = 0; dir != 6; dir++) {
1279 int flag = 1 << dir;
1280 v3s16 p2 = blockpos_nodes + p + connection_dirs[dir];
1281 MapNode n2 = data->m_vmanip.getNodeNoEx(p2);
1282 if (nodedef->nodeboxConnects(n, n2, flag))
1283 neighbors_set |= flag;
1287 std::vector<aabb3f> boxes;
1288 n.getNodeBoxes(nodedef, &boxes, neighbors_set);
1289 for (const auto &box : boxes)
1290 drawAutoLightedCuboid(box, nullptr, tiles, 6);
1293 void MapblockMeshGenerator::drawMeshNode()
1297 bool private_mesh; // as a grab/drop pair is not thread-safe
1299 if (f->param_type_2 == CPT2_FACEDIR ||
1300 f->param_type_2 == CPT2_COLORED_FACEDIR) {
1301 facedir = n.getFaceDir(nodedef);
1302 } else if (f->param_type_2 == CPT2_WALLMOUNTED ||
1303 f->param_type_2 == CPT2_COLORED_WALLMOUNTED) {
1304 // Convert wallmounted to 6dfacedir.
1305 // When cache enabled, it is already converted.
1306 facedir = n.getWallMounted(nodedef);
1307 if (!enable_mesh_cache)
1308 facedir = wallmounted_to_facedir[facedir];
1311 if (!data->m_smooth_lighting && f->mesh_ptr[facedir]) {
1312 // use cached meshes
1313 private_mesh = false;
1314 mesh = f->mesh_ptr[facedir];
1315 } else if (f->mesh_ptr[0]) {
1316 // no cache, clone and rotate mesh
1317 private_mesh = true;
1318 mesh = cloneMesh(f->mesh_ptr[0]);
1319 rotateMeshBy6dFacedir(mesh, facedir);
1320 recalculateBoundingBox(mesh);
1321 meshmanip->recalculateNormals(mesh, true, false);
1325 int mesh_buffer_count = mesh->getMeshBufferCount();
1326 for (int j = 0; j < mesh_buffer_count; j++) {
1328 scene::IMeshBuffer *buf = mesh->getMeshBuffer(j);
1329 video::S3DVertex *vertices = (video::S3DVertex *)buf->getVertices();
1330 int vertex_count = buf->getVertexCount();
1332 if (data->m_smooth_lighting) {
1333 // Mesh is always private here. So the lighting is applied to each
1334 // vertex right here.
1335 for (int k = 0; k < vertex_count; k++) {
1336 video::S3DVertex &vertex = vertices[k];
1337 vertex.Color = blendLightColor(vertex.Pos, vertex.Normal);
1338 vertex.Pos += origin;
1340 collector->append(tile, vertices, vertex_count,
1341 buf->getIndices(), buf->getIndexCount());
1343 // Don't modify the mesh, it may not be private here.
1344 // Instead, let the collector process colors, etc.
1345 collector->append(tile, vertices, vertex_count,
1346 buf->getIndices(), buf->getIndexCount(), origin,
1347 color, f->light_source);
1354 // also called when the drawtype is known but should have been pre-converted
1355 void MapblockMeshGenerator::errorUnknownDrawtype()
1357 infostream << "Got drawtype " << f->drawtype << std::endl;
1358 FATAL_ERROR("Unknown drawtype");
1361 void MapblockMeshGenerator::drawNode()
1363 // skip some drawtypes early
1364 switch (f->drawtype) {
1365 case NDT_NORMAL: // Drawn by MapBlockMesh
1366 case NDT_AIRLIKE: // Not drawn at all
1367 case NDT_LIQUID: // Drawn by MapBlockMesh
1372 origin = intToFloat(p, BS);
1373 if (data->m_smooth_lighting)
1374 getSmoothLightFrame();
1376 light = LightPair(getInteriorLight(n, 1, nodedef));
1377 switch (f->drawtype) {
1378 case NDT_FLOWINGLIQUID: drawLiquidNode(); break;
1379 case NDT_GLASSLIKE: drawGlasslikeNode(); break;
1380 case NDT_GLASSLIKE_FRAMED: drawGlasslikeFramedNode(); break;
1381 case NDT_ALLFACES: drawAllfacesNode(); break;
1382 case NDT_TORCHLIKE: drawTorchlikeNode(); break;
1383 case NDT_SIGNLIKE: drawSignlikeNode(); break;
1384 case NDT_PLANTLIKE: drawPlantlikeNode(); break;
1385 case NDT_PLANTLIKE_ROOTED: drawPlantlikeRootedNode(); break;
1386 case NDT_FIRELIKE: drawFirelikeNode(); break;
1387 case NDT_FENCELIKE: drawFencelikeNode(); break;
1388 case NDT_RAILLIKE: drawRaillikeNode(); break;
1389 case NDT_NODEBOX: drawNodeboxNode(); break;
1390 case NDT_MESH: drawMeshNode(); break;
1391 default: errorUnknownDrawtype(); break;
1396 TODO: Fix alpha blending for special nodes
1397 Currently only the last element rendered is blended correct
1399 void MapblockMeshGenerator::generate()
1401 for (p.Z = 0; p.Z < MAP_BLOCKSIZE; p.Z++)
1402 for (p.Y = 0; p.Y < MAP_BLOCKSIZE; p.Y++)
1403 for (p.X = 0; p.X < MAP_BLOCKSIZE; p.X++) {
1404 n = data->m_vmanip.getNodeNoEx(blockpos_nodes + p);
1405 f = &nodedef->get(n);
1410 void MapblockMeshGenerator::renderSingle(content_t node)
1413 n = MapNode(node, 0xff, 0x00);
1414 f = &nodedef->get(n);