From d17f22f536cf5a94dda88d4f36b1faf0bb44958e Mon Sep 17 00:00:00 2001 From: cx384 Date: Sat, 24 May 2025 15:59:32 +0200 Subject: [PATCH] Fix texture coordinates of cuboid drawtypes (#16091) Fixes issues related to combining animated and world-aligned textures. Changes texture coordinates of cuboid drawtypes to stay in the [0,1] range, instead of carrying the mapblock alignment and becoming negative after transformations. --- src/client/content_mapblock.cpp | 111 +++++++++++++++++++++---------- src/client/content_mapblock.h | 2 +- src/client/meshgen/collector.cpp | 12 +--- src/client/meshgen/collector.h | 2 +- 4 files changed, 80 insertions(+), 47 deletions(-) diff --git a/src/client/content_mapblock.cpp b/src/client/content_mapblock.cpp index 3edba95e3..c07a0a3b1 100644 --- a/src/client/content_mapblock.cpp +++ b/src/client/content_mapblock.cpp @@ -137,11 +137,20 @@ void MapblockMeshGenerator::drawQuad(const TileSpec &tile, v3f *coords, const v3 } static std::array setupCuboidVertices(const aabb3f &box, - const f32 *txc, const TileSpec *tiles, int tilecount) + const f32 *txc, const TileSpec *tiles, int tilecount, v3s16 alignment) { v3f min = box.MinEdge; v3f max = box.MaxEdge; + // Texture coords are [0,1] if not specified otherwise + f32 uniform_txc[24]; + if (!txc) { + for (int i = 0; i != 24; ++i) { + uniform_txc[i] = (i % 4 < 2) ? 0.0f : 1.0f; + } + txc = uniform_txc; + } + std::array vertices = {{ // top video::S3DVertex(min.X, max.Y, max.Z, 0, 1, 0, {}, txc[0], txc[1]), @@ -185,15 +194,47 @@ static std::array setupCuboidVertices(const aabb3f &box, case TileRotation::None: break; case TileRotation::R90: - tcoords.set(-tcoords.Y, tcoords.X); + tcoords.set(1 - tcoords.Y, tcoords.X); break; case TileRotation::R180: - tcoords.set(-tcoords.X, -tcoords.Y); + tcoords.set(1 - tcoords.X, 1 - tcoords.Y); break; case TileRotation::R270: - tcoords.set(tcoords.Y, -tcoords.X); + tcoords.set(tcoords.Y, 1 - tcoords.X); break; } + + if (tile.world_aligned) { + // Maps uv dimension of every face to world dimension xyz + constexpr int coord_dim[12] = { + 0, 2, // up + 0, 2, // down + 2, 1, // right + 2, 1, // left + 0, 1, // back + 0, 1, // front + }; + + auto scale = tile.layers[0].scale; + f32 scale_factor = 1.0f / scale; + + float x = alignment[coord_dim[face*2]] % scale; + float y = alignment[coord_dim[face*2 + 1]] % scale; + + // Faces grow in different directions + if (face != 1) { + y = tcoords.Y + ((scale-1)-y); + } else { + y = tcoords.Y + y; + } + if (face == 3 || face == 4) { + x = tcoords.X + ((scale-1)-x); + } else { + x = tcoords.X + x; + } + + tcoords.set(x * scale_factor, y * scale_factor); + } } } @@ -212,6 +253,7 @@ enum class QuadDiagonal { // for the opposite corners of each face - therefore, there // 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 +// if nullptr use standard [0,1] coords // (compatible with ContentFeatures). // mask - a bit mask that suppresses drawing of tiles. // tile i will not be drawn if mask & (1 << i) is 1 @@ -224,7 +266,7 @@ void MapblockMeshGenerator::drawCuboid(const aabb3f &box, { assert(tilecount >= 1 && tilecount <= 6); // pre-condition - auto vertices = setupCuboidVertices(box, txc, tiles, tilecount); + auto vertices = setupCuboidVertices(box, txc, tiles, tilecount, cur_node.p); for (int k = 0; k < 6; ++k) { if (mask & (1 << k)) @@ -301,12 +343,13 @@ video::SColor MapblockMeshGenerator::blendLightColor(const v3f &vertex_pos, void MapblockMeshGenerator::generateCuboidTextureCoords(const aabb3f &box, f32 *coords) { - f32 tx1 = (box.MinEdge.X / BS) + 0.5; - f32 ty1 = (box.MinEdge.Y / BS) + 0.5; - f32 tz1 = (box.MinEdge.Z / BS) + 0.5; - f32 tx2 = (box.MaxEdge.X / BS) + 0.5; - f32 ty2 = (box.MaxEdge.Y / BS) + 0.5; - f32 tz2 = (box.MaxEdge.Z / BS) + 0.5; + // Generate texture coords which are aligned to coords of a solid nodes + f32 tx1 = (box.MinEdge.X / BS) + 0.5f; + f32 ty1 = (box.MinEdge.Y / BS) + 0.5f; + f32 tz1 = (box.MinEdge.Z / BS) + 0.5f; + f32 tx2 = (box.MaxEdge.X / BS) + 0.5f; + f32 ty2 = (box.MaxEdge.Y / BS) + 0.5f; + f32 tz2 = (box.MaxEdge.Z / BS) + 0.5f; f32 txc[24] = { tx1, 1 - tz2, tx2, 1 - tz1, // up tx1, tz1, tx2, tz2, // down @@ -334,7 +377,6 @@ void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, const TileSpec *tiles, int tile_count, const f32 *txc, u8 mask) { bool scale = std::fabs(cur_node.f->visual_scale - 1.0f) > 1e-3f; - f32 texture_coord_buf[24]; f32 dx1 = box.MinEdge.X; f32 dy1 = box.MinEdge.Y; f32 dz1 = box.MinEdge.Z; @@ -342,19 +384,11 @@ void MapblockMeshGenerator::drawAutoLightedCuboid(aabb3f box, f32 dy2 = box.MaxEdge.Y; f32 dz2 = box.MaxEdge.Z; if (scale) { - if (!txc) { // generate texture coords before scaling - generateCuboidTextureCoords(box, texture_coord_buf); - txc = texture_coord_buf; - } box.MinEdge *= cur_node.f->visual_scale; box.MaxEdge *= cur_node.f->visual_scale; } box.MinEdge += cur_node.origin; box.MaxEdge += cur_node.origin; - if (!txc) { - generateCuboidTextureCoords(box, texture_coord_buf); - txc = texture_coord_buf; - } if (data->m_smooth_lighting) { LightInfo lights[8]; for (int j = 0; j < 8; ++j) { @@ -442,10 +476,8 @@ void MapblockMeshGenerator::drawSolidNode() u8 mask = faces ^ 0b0011'1111; // k-th bit is set if k-th face is to be *omitted*, as expected by cuboid drawing functions. cur_node.origin = intToFloat(cur_node.p, BS); auto box = aabb3f(v3f(-0.5 * BS), v3f(0.5 * BS)); - f32 texture_coord_buf[24]; box.MinEdge += cur_node.origin; box.MaxEdge += cur_node.origin; - generateCuboidTextureCoords(box, texture_coord_buf); if (data->m_smooth_lighting) { LightPair lights[6][4]; for (int face = 0; face < 6; ++face) { @@ -458,7 +490,7 @@ void MapblockMeshGenerator::drawSolidNode() } } - drawCuboid(box, tiles, 6, texture_coord_buf, mask, [&] (int face, video::S3DVertex vertices[4]) { + drawCuboid(box, tiles, 6, nullptr, mask, [&] (int face, video::S3DVertex vertices[4]) { auto final_lights = lights[face]; for (int j = 0; j < 4; j++) { video::S3DVertex &vertex = vertices[j]; @@ -471,7 +503,7 @@ void MapblockMeshGenerator::drawSolidNode() return QuadDiagonal::Diag02; }); } else { - drawCuboid(box, tiles, 6, texture_coord_buf, mask, [&] (int face, video::S3DVertex vertices[4]) { + drawCuboid(box, tiles, 6, nullptr, mask, [&] (int face, video::S3DVertex vertices[4]) { video::SColor color = encode_light(lights[face], cur_node.f->light_source); if (!cur_node.f->light_source) applyFacesShading(color, vertices[0].Normal); @@ -952,7 +984,10 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode() edge_invisible = nb[nb_triplet[edge][0]] ^ nb[nb_triplet[edge][1]]; if (edge_invisible) continue; - drawAutoLightedCuboid(frame_edges[edge], tiles[1]); + + f32 txc[24]; + generateCuboidTextureCoords(frame_edges[edge], txc); + drawAutoLightedCuboid(frame_edges[edge], tiles[1], txc); } for (int face = 0; face < 6; face++) { @@ -996,16 +1031,17 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode() float vlev = (param2 / 63.0f) * 2.0f - 1.0f; TileSpec tile; getSpecialTile(0, &tile); - drawAutoLightedCuboid( - aabb3f( - -(nb[5] ? g : b), - -(nb[4] ? g : b), - -(nb[3] ? g : b), - (nb[2] ? g : b), - (nb[1] ? g : b) * vlev, - (nb[0] ? g : b) - ), - tile); + aabb3f box( + -(nb[5] ? g : b), + -(nb[4] ? g : b), + -(nb[3] ? g : b), + (nb[2] ? g : b), + (nb[1] ? g : b) * vlev, + (nb[0] ? g : b) + ); + f32 txc[24]; + generateCuboidTextureCoords(box, txc); + drawAutoLightedCuboid(box, tile, txc); } } @@ -1649,7 +1685,10 @@ void MapblockMeshGenerator::drawNodeboxNode() for (auto &box : boxes) { u8 mask = getNodeBoxMask(box, solid_neighbors, sametype_neighbors); - drawAutoLightedCuboid(box, tiles, 6, nullptr, mask); + + f32 txc[24]; + generateCuboidTextureCoords(box, txc); + drawAutoLightedCuboid(box, tiles, 6, txc, mask); } } diff --git a/src/client/content_mapblock.h b/src/client/content_mapblock.h index a4ed6a0fc..9d51ba2bc 100644 --- a/src/client/content_mapblock.h +++ b/src/client/content_mapblock.h @@ -86,7 +86,7 @@ private: template void drawCuboid(const aabb3f &box, const TileSpec *tiles, int tilecount, const f32 *txc, u8 mask, Fn &&face_lighter); - void generateCuboidTextureCoords(aabb3f const &box, f32 *coords); + static void generateCuboidTextureCoords(aabb3f const &box, f32 *coords); void drawAutoLightedCuboid(aabb3f box, const TileSpec &tile, f32 const *txc = nullptr, u8 mask = 0); void drawAutoLightedCuboid(aabb3f box, const TileSpec *tiles, int tile_count, f32 const *txc = nullptr, u8 mask = 0); u8 getNodeBoxMask(aabb3f box, u8 solid_neighbors, u8 sametype_neighbors) const; diff --git a/src/client/meshgen/collector.cpp b/src/client/meshgen/collector.cpp index c8b726cde..6e3002624 100644 --- a/src/client/meshgen/collector.cpp +++ b/src/client/meshgen/collector.cpp @@ -14,25 +14,19 @@ void MeshCollector::append(const TileSpec &tile, const video::S3DVertex *vertice const TileLayer *layer = &tile.layers[layernum]; if (layer->empty()) continue; - append(*layer, vertices, numVertices, indices, numIndices, layernum, - tile.world_aligned); + append(*layer, vertices, numVertices, indices, numIndices, layernum); } } void MeshCollector::append(const TileLayer &layer, const video::S3DVertex *vertices, - u32 numVertices, const u16 *indices, u32 numIndices, u8 layernum, - bool use_scale) + u32 numVertices, const u16 *indices, u32 numIndices, u8 layernum) { PreMeshBuffer &p = findBuffer(layer, layernum, numVertices); - f32 scale = 1.0f; - if (use_scale) - scale = 1.0f / layer.scale; - u32 vertex_count = p.vertices.size(); for (u32 i = 0; i < numVertices; i++) { p.vertices.emplace_back(vertices[i].Pos + offset, vertices[i].Normal, - vertices[i].Color, scale * vertices[i].TCoords); + vertices[i].Color, vertices[i].TCoords); m_bounding_radius_sq = std::max(m_bounding_radius_sq, (vertices[i].Pos - m_center_pos).getLengthSQ()); } diff --git a/src/client/meshgen/collector.h b/src/client/meshgen/collector.h index f1f8a1481..876338baa 100644 --- a/src/client/meshgen/collector.h +++ b/src/client/meshgen/collector.h @@ -55,7 +55,7 @@ private: void append(const TileLayer &material, const video::S3DVertex *vertices, u32 numVertices, const u16 *indices, u32 numIndices, - u8 layernum, bool use_scale = false); + u8 layernum); PreMeshBuffer &findBuffer(const TileLayer &layer, u8 layernum, u32 numVertices); };