Avoid rendering invisible faces of simple nodeboxes (#12262)

* Skip rendering faces adjacent to opaque nodes
* Cancel out opposite faces of adjacent nodebox nodes of the same type

Fixes #6409
This commit is contained in:
x2048 2022-05-04 23:44:55 +02:00 committed by GitHub
parent 89c82035d8
commit cc56ebd90d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 82 additions and 13 deletions

@ -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 // 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 // the faces in the list is up-down-right-left-back-front
// (compatible with ContentFeatures). // (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, 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 assert(tilecount >= 1 && tilecount <= 6); // pre-condition
@ -274,6 +276,8 @@ void MapblockMeshGenerator::drawCuboid(const aabb3f &box,
// Add to mesh collector // Add to mesh collector
for (int k = 0; k < 6; ++k) { for (int k = 0; k < 6; ++k) {
if (mask & (1 << k))
continue;
int tileindex = MYMIN(k, tilecount - 1); int tileindex = MYMIN(k, tilecount - 1);
collector->append(tiles[tileindex], vertices + 4 * k, 4, quad_indices, 6); 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, 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; bool scale = std::fabs(f->visual_scale - 1.0f) > 1e-3f;
f32 texture_coord_buf[24]; f32 texture_coord_buf[24];
@ -403,12 +407,49 @@ void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const f32 *txc,
d.Z = (j & 1) ? dz2 : dz1; d.Z = (j & 1) ? dz2 : dz1;
lights[j] = blendLight(d); lights[j] = blendLight(d);
} }
drawCuboid(box, tiles, tile_count, lights, txc); drawCuboid(box, tiles, tile_count, lights, txc, mask);
} else { } 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() void MapblockMeshGenerator::prepareLiquidNodeDrawing()
{ {
getSpecialTile(0, &tile_liquid_top); getSpecialTile(0, &tile_liquid_top);
@ -1366,13 +1407,38 @@ void MapblockMeshGenerator::drawNodeboxNode()
getTile(nodebox_tile_dirs[face], &tiles[face]); 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 // locate possible neighboring nodes to connect to
u8 neighbors_set = 0; u8 neighbors_set = 0;
if (f->node_box.type == NODEBOX_CONNECTED) { u8 solid_neighbors = 0;
u8 sametype_neighbors = 0;
for (int dir = 0; dir != 6; dir++) { for (int dir = 0; dir != 6; dir++) {
u8 flag = 1 << dir; u8 flag = 1 << dir;
v3s16 p2 = blockpos_nodes + p + nodebox_connection_dirs[dir]; v3s16 p2 = blockpos_nodes + p + nodebox_tile_dirs[dir];
MapNode n2 = data->m_vmanip.getNodeNoEx(p2); 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)) if (nodedef->nodeboxConnects(n, n2, flag))
neighbors_set |= flag; neighbors_set |= flag;
} }
@ -1433,8 +1499,10 @@ void MapblockMeshGenerator::drawNodeboxNode()
} }
} }
for (auto &box : boxes) for (auto &box : boxes) {
drawAutoLightedCuboid(box, nullptr, tiles, 6); u8 mask = getNodeBoxMask(box, solid_neighbors, sametype_neighbors);
drawAutoLightedCuboid(box, nullptr, tiles, 6, mask);
}
} }
void MapblockMeshGenerator::drawMeshNode() void MapblockMeshGenerator::drawMeshNode()

@ -100,10 +100,11 @@ public:
// cuboid drawing! // cuboid drawing!
void drawCuboid(const aabb3f &box, TileSpec *tiles, int tilecount, void drawCuboid(const aabb3f &box, TileSpec *tiles, int tilecount,
const LightInfo *lights , const f32 *txc); const LightInfo *lights , const f32 *txc, u8 mask = 0);
void generateCuboidTextureCoords(aabb3f const &box, f32 *coords); void generateCuboidTextureCoords(aabb3f const &box, f32 *coords);
void drawAutoLightedCuboid(aabb3f box, const f32 *txc = NULL, void drawAutoLightedCuboid(aabb3f box, const f32 *txc = NULL,
TileSpec *tiles = NULL, int tile_count = 0); TileSpec *tiles = NULL, int tile_count = 0, u8 mask = 0);
u8 getNodeBoxMask(aabb3f box, u8 solid_neighbors, u8 sametype_neighbors) const;
// liquid-specific // liquid-specific
bool top_is_same_liquid; bool top_is_same_liquid;