summaryrefslogtreecommitdiff
path: root/src/client/content_mapblock.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/content_mapblock.cpp')
-rw-r--r--src/client/content_mapblock.cpp143
1 files changed, 132 insertions, 11 deletions
diff --git a/src/client/content_mapblock.cpp b/src/client/content_mapblock.cpp
index bb2d6398f..0bac5e827 100644
--- a/src/client/content_mapblock.cpp
+++ b/src/client/content_mapblock.cpp
@@ -150,8 +150,10 @@ void MapblockMeshGenerator::drawQuad(v3f *coords, const v3s16 &normal,
// should be (2+2)*6=24 values in the list. The order of
// the faces in the list is up-down-right-left-back-front
// (compatible with ContentFeatures).
+// mask - a bit mask that suppresses drawing of tiles.
+// tile i will not be drawn if mask & (1 << i) is 1
void MapblockMeshGenerator::drawCuboid(const aabb3f &box,
- TileSpec *tiles, int tilecount, const LightInfo *lights, const f32 *txc)
+ TileSpec *tiles, int tilecount, const LightInfo *lights, const f32 *txc, u8 mask)
{
assert(tilecount >= 1 && tilecount <= 6); // pre-condition
@@ -274,6 +276,8 @@ void MapblockMeshGenerator::drawCuboid(const aabb3f &box,
// Add to mesh collector
for (int k = 0; k < 6; ++k) {
+ if (mask & (1 << k))
+ continue;
int tileindex = MYMIN(k, tilecount - 1);
collector->append(tiles[tileindex], vertices + 4 * k, 4, quad_indices, 6);
}
@@ -363,7 +367,7 @@ void MapblockMeshGenerator::generateCuboidTextureCoords(const aabb3f &box, f32 *
}
void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc,
- TileSpec *tiles, int tile_count)
+ TileSpec *tiles, int tile_count, u8 mask)
{
bool scale = std::fabs(f->visual_scale - 1.0f) > 1e-3f;
f32 texture_coord_buf[24];
@@ -400,12 +404,49 @@ void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc,
d.Z = (j & 1) ? dz2 : dz1;
lights[j] = blendLight(d);
}
- drawCuboid(box, tiles, tile_count, lights, txc);
+ drawCuboid(box, tiles, tile_count, lights, txc, mask);
} else {
- drawCuboid(box, tiles, tile_count, nullptr, txc);
+ drawCuboid(box, tiles, tile_count, nullptr, txc, mask);
}
}
+u8 MapblockMeshGenerator::getNodeBoxMask(aabb3f box, u8 solid_neighbors, u8 sametype_neighbors) const
+{
+ const f32 NODE_BOUNDARY = 0.5 * BS;
+
+ // For an oversized nodebox, return immediately
+ if (box.MaxEdge.X > NODE_BOUNDARY ||
+ box.MinEdge.X < -NODE_BOUNDARY ||
+ box.MaxEdge.Y > NODE_BOUNDARY ||
+ box.MinEdge.Y < -NODE_BOUNDARY ||
+ box.MaxEdge.Z > NODE_BOUNDARY ||
+ box.MinEdge.Z < -NODE_BOUNDARY)
+ return 0;
+
+ // We can skip faces at node boundary if the matching neighbor is solid
+ u8 solid_mask =
+ (box.MaxEdge.Y == NODE_BOUNDARY ? 1 : 0) |
+ (box.MinEdge.Y == -NODE_BOUNDARY ? 2 : 0) |
+ (box.MaxEdge.X == NODE_BOUNDARY ? 4 : 0) |
+ (box.MinEdge.X == -NODE_BOUNDARY ? 8 : 0) |
+ (box.MaxEdge.Z == NODE_BOUNDARY ? 16 : 0) |
+ (box.MinEdge.Z == -NODE_BOUNDARY ? 32 : 0);
+
+ u8 sametype_mask = 0;
+ if (f->alpha == AlphaMode::ALPHAMODE_OPAQUE) {
+ // In opaque nodeboxes, faces on opposite sides can cancel
+ // each other out if there is a matching neighbor of the same type
+ sametype_mask =
+ ((solid_mask & 3) == 3 ? 3 : 0) |
+ ((solid_mask & 12) == 12 ? 12 : 0) |
+ ((solid_mask & 48) == 48 ? 48 : 0);
+ }
+
+ // Combine masks with actual neighbors to get the faces to be skipped
+ return (solid_mask & solid_neighbors) | (sametype_mask & sametype_neighbors);
+}
+
+
void MapblockMeshGenerator::prepareLiquidNodeDrawing()
{
getSpecialTile(0, &tile_liquid_top);
@@ -1363,13 +1404,38 @@ void MapblockMeshGenerator::drawNodeboxNode()
getTile(nodebox_tile_dirs[face], &tiles[face]);
}
+ bool param2_is_rotation =
+ f->param_type_2 == CPT2_COLORED_FACEDIR ||
+ f->param_type_2 == CPT2_COLORED_WALLMOUNTED ||
+ f->param_type_2 == CPT2_FACEDIR ||
+ f->param_type_2 == CPT2_WALLMOUNTED;
+
+ bool param2_is_level =
+ f->param_type_2 == CPT2_LEVELED;
+
// locate possible neighboring nodes to connect to
u8 neighbors_set = 0;
- if (f->node_box.type == NODEBOX_CONNECTED) {
- for (int dir = 0; dir != 6; dir++) {
- u8 flag = 1 << dir;
- v3s16 p2 = blockpos_nodes + p + nodebox_connection_dirs[dir];
- MapNode n2 = data->m_vmanip.getNodeNoEx(p2);
+ u8 solid_neighbors = 0;
+ u8 sametype_neighbors = 0;
+ for (int dir = 0; dir != 6; dir++) {
+ u8 flag = 1 << dir;
+ v3s16 p2 = blockpos_nodes + p + nodebox_tile_dirs[dir];
+ MapNode n2 = data->m_vmanip.getNodeNoEx(p2);
+
+ // mark neighbors that are the same node type
+ // and have the same rotation or higher level stored as param2
+ if (n2.param0 == n.param0 &&
+ (!param2_is_rotation || n.param2 == n2.param2) &&
+ (!param2_is_level || n.param2 <= n2.param2))
+ sametype_neighbors |= flag;
+
+ // mark neighbors that are simple solid blocks
+ if (nodedef->get(n2).drawtype == NDT_NORMAL)
+ solid_neighbors |= flag;
+
+ if (f->node_box.type == NODEBOX_CONNECTED) {
+ p2 = blockpos_nodes + p + nodebox_connection_dirs[dir];
+ n2 = data->m_vmanip.getNodeNoEx(p2);
if (nodedef->nodeboxConnects(n, n2, flag))
neighbors_set |= flag;
}
@@ -1377,8 +1443,63 @@ void MapblockMeshGenerator::drawNodeboxNode()
std::vector<aabb3f> boxes;
n.getNodeBoxes(nodedef, &boxes, neighbors_set);
- for (auto &box : boxes)
- drawAutoLightedCuboid(box, nullptr, tiles, 6);
+
+ bool isTransparent = false;
+
+ for (const TileSpec &tile : tiles) {
+ if (tile.layers[0].isTransparent()) {
+ isTransparent = true;
+ break;
+ }
+ }
+
+ if (isTransparent) {
+ std::vector<float> sections;
+ // Preallocate 8 default splits + Min&Max for each nodebox
+ sections.reserve(8 + 2 * boxes.size());
+
+ for (int axis = 0; axis < 3; axis++) {
+ // identify sections
+
+ if (axis == 0) {
+ // Default split at node bounds, up to 3 nodes in each direction
+ for (float s = -3.5f * BS; s < 4.0f * BS; s += 1.0f * BS)
+ sections.push_back(s);
+ }
+ else {
+ // Avoid readding the same 8 default splits for Y and Z
+ sections.resize(8);
+ }
+
+ // Add edges of existing node boxes, rounded to 1E-3
+ for (size_t i = 0; i < boxes.size(); i++) {
+ sections.push_back(std::floor(boxes[i].MinEdge[axis] * 1E3) * 1E-3);
+ sections.push_back(std::floor(boxes[i].MaxEdge[axis] * 1E3) * 1E-3);
+ }
+
+ // split the boxes at recorded sections
+ // limit splits to avoid runaway crash if inner loop adds infinite splits
+ // due to e.g. precision problems.
+ // 100 is just an arbitrary, reasonably high number.
+ for (size_t i = 0; i < boxes.size() && i < 100; i++) {
+ aabb3f *box = &boxes[i];
+ for (float section : sections) {
+ if (box->MinEdge[axis] < section && box->MaxEdge[axis] > section) {
+ aabb3f copy(*box);
+ copy.MinEdge[axis] = section;
+ box->MaxEdge[axis] = section;
+ boxes.push_back(copy);
+ box = &boxes[i]; // find new address of the box in case of reallocation
+ }
+ }
+ }
+ }
+ }
+
+ for (auto &box : boxes) {
+ u8 mask = getNodeBoxMask(box, solid_neighbors, sametype_neighbors);
+ drawAutoLightedCuboid(box, nullptr, tiles, 6, mask);
+ }
}
void MapblockMeshGenerator::drawMeshNode()