From d982ba9ec47d4c96647c5e1a7b8c90192ed33eda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 21 Jun 2023 16:37:51 +0200 Subject: [PATCH] MeshTools: support morph targets in concatenate(). While extremely easy to do with the linear lookup, I bet it'd be something utterly complex like trying to hash a std::pair with the original hashmap-based implementation. --- src/Magnum/MeshTools/Concatenate.cpp | 14 +-- src/Magnum/MeshTools/Test/ConcatenateTest.cpp | 110 ++++++++++++++---- 2 files changed, 93 insertions(+), 31 deletions(-) diff --git a/src/Magnum/MeshTools/Concatenate.cpp b/src/Magnum/MeshTools/Concatenate.cpp index 56d0ed5f8..f748e8748 100644 --- a/src/Magnum/MeshTools/Concatenate.cpp +++ b/src/Magnum/MeshTools/Concatenate.cpp @@ -123,14 +123,14 @@ Trade::MeshData concatenate(Containers::Array&& indexData, const UnsignedI any equivalent in the destination mesh */ for(UnsignedInt src = 0; src != mesh.attributeCount(); ++src) { /* Try to find a matching attribute in the destination mesh (same - name, same set). Skip if no such attribute is found. This is a - O(m + n) complexity (linear lookup in both the source and the - output mesh), but given the assumption that meshes rarely have - more than 8-16 attributes it should still be faster than - building a hashmap first and then doing a complex lookup in it - (which is how it used to be before, using + name, same set, same morph target ID). Skip if no such attribute + is found. This is a O(m + n) complexity (linear lookup in both + the source and the output mesh), but given the assumption that + meshes rarely have more than 8-16 attributes it should still be + faster than building a hashmap first and then doing a complex + lookup in it (which is how it used to be before, using std::unordered_multimap). */ - const Containers::Optional dst = out.findAttributeId(mesh.attributeName(src), mesh.attributeId(src)); + const Containers::Optional dst = out.findAttributeId(mesh.attributeName(src), mesh.attributeId(src), mesh.attributeMorphTargetId(src)); if(!dst) continue; diff --git a/src/Magnum/MeshTools/Test/ConcatenateTest.cpp b/src/Magnum/MeshTools/Test/ConcatenateTest.cpp index 184157f29..9a243f22d 100644 --- a/src/Magnum/MeshTools/Test/ConcatenateTest.cpp +++ b/src/Magnum/MeshTools/Test/ConcatenateTest.cpp @@ -98,6 +98,7 @@ struct VertexDataA { Vector2 texcoords2; Int:32; Vector3 position; + Vector2 positionMorphTarget; Short data[3]; }; @@ -110,13 +111,24 @@ void ConcatenateTest::concatenate() { /* First is non-indexed, this layout (including the gap) will be preserved */ const VertexDataA vertexDataA[]{ - {{0.1f, 0.2f}, {0.5f, 0.6f}, {1.0f, 2.0f, 3.0f}, {15, 3, -1}}, - {{0.3f, 0.4f}, {0.7f, 0.8f}, {4.0f, 5.0f, 6.0f}, {14, 2, -4}} + {{0.1f, 0.2f}, + {0.5f, 0.6f}, + {1.0f, 2.0f, 3.0f}, + {3.0f, 1.0f}, + {15, 3, -1}}, + {{0.3f, 0.4f}, + {0.7f, 0.8f}, + {4.0f, 5.0f, 6.0f}, + {6.0f, 4.0f}, + {14, 2, -4}} }; Containers::StridedArrayView1D verticesA = vertexDataA; Trade::MeshData a{MeshPrimitive::Points, {}, vertexDataA, { Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates, verticesA.slice(&VertexDataA::texcoords1)}, + /* Morph target to verify it's correctly propagated */ + Trade::MeshAttributeData{Trade::MeshAttribute::Position, + verticesA.slice(&VertexDataA::positionMorphTarget), 37}, Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates, verticesA.slice(&VertexDataA::texcoords2)}, Trade::MeshAttributeData{Trade::MeshAttribute::Position, @@ -127,17 +139,36 @@ void ConcatenateTest::concatenate() { }}; /* Second is indexed, has only one texture coordinate of the two, an extra - color (which gets ignored) and misses the position (which will be - zero-filled) */ + color (which gets ignored) misses the position (which will be + zero-filled) but contains two morph target positions, of which only + one will get used, the one with a matching ID */ const struct VertexDataB { Color4 color; Short data[3]; Vector2 texcoords1; + Vector2 positionMorphTarget1; + Vector2 positionMorphTarget2; } vertexDataB[]{ - {0x112233_rgbf, {28, -15, 0}, {0.15f, 0.25f}}, - {0x445566_rgbf, {29, -16, 1}, {0.35f, 0.45f}}, - {0x778899_rgbf, {30, -17, 2}, {0.55f, 0.65f}}, - {0xaabbcc_rgbf, {40, -18, 3}, {0.75f, 0.85f}} + {0x112233_rgbf, + {28, -15, 0}, + {0.15f, 0.25f}, + {}, + {9.0f, 7.0f}}, + {0x445566_rgbf, + {29, -16, 1}, + {0.35f, 0.45f}, + {}, + {0.0f, 2.0f}}, + {0x778899_rgbf, + {30, -17, 2}, + {0.55f, 0.65f}, + {}, + {5.0f, 8.0f}}, + {0xaabbcc_rgbf, + {40, -18, 3}, + {0.75f, 0.85f}, + {}, + {2.0f, 0.0f}} }; Containers::StridedArrayView1D verticesB = vertexDataB; const UnsignedShort indicesB[]{0, 2, 1, 0, 3, 2}; @@ -149,7 +180,12 @@ void ConcatenateTest::concatenate() { Trade::MeshAttributeData{Trade::meshAttributeCustom(42), VertexFormat::Short, verticesB.slice(&VertexDataB::data), 3}, Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates, - verticesB.slice(&VertexDataB::texcoords1)} + verticesB.slice(&VertexDataB::texcoords1)}, + /* Morph targets to verify they're correctly propagated */ + Trade::MeshAttributeData{Trade::MeshAttribute::Position, + verticesB.slice(&VertexDataB::positionMorphTarget1), 22}, + Trade::MeshAttributeData{Trade::MeshAttribute::Position, + verticesB.slice(&VertexDataB::positionMorphTarget2), 37} }}; /* Third is again non-indexed, has one texcoord attribute more (which will @@ -163,9 +199,21 @@ void ConcatenateTest::concatenate() { Vector2 texcoords1; Short data[1]; } vertexDataC[]{ - {{0.425f, 0.475f}, {1.5f, 2.5f, 3.5f}, {0.725f, 0.775f}, {0.125f, 0.175f}, {320}}, - {{0.525f, 0.575f}, {4.5f, 5.5f, 6.5f}, {0.825f, 0.875f}, {0.225f, 0.275f}, {3200}}, - {{0.625f, 0.675f}, {7.5f, 8.5f, 9.5f}, {0.925f, 0.975f}, {0.325f, 0.375f}, {32000}}, + {{0.425f, 0.475f}, + {1.5f, 2.5f, 3.5f}, + {0.725f, 0.775f}, + {0.125f, 0.175f}, + {320}}, + {{0.525f, 0.575f}, + {4.5f, 5.5f, 6.5f}, + {0.825f, 0.875f}, + {0.225f, 0.275f}, + {3200}}, + {{0.625f, 0.675f}, + {7.5f, 8.5f, 9.5f}, + {0.925f, 0.975f}, + {0.325f, 0.375f}, + {32000}}, }; Containers::StridedArrayView1D verticesC = vertexDataC; Trade::MeshData c{MeshPrimitive::Points, {}, vertexDataC, { @@ -188,7 +236,7 @@ void ConcatenateTest::concatenate() { MeshTools::concatenate({a, b, c}); CORRADE_COMPARE(dst.primitive(), MeshPrimitive::Points); - CORRADE_COMPARE(dst.attributeCount(), 4); + CORRADE_COMPARE(dst.attributeCount(), 5); CORRADE_COMPARE_AS(dst.attribute(Trade::MeshAttribute::Position), Containers::arrayView({ {1.0f, 2.0f, 3.0f}, @@ -198,6 +246,16 @@ void ConcatenateTest::concatenate() { {4.5f, 5.5f, 6.5f}, {7.5f, 8.5f, 9.5f} }), TestSuite::Compare::Container); + CORRADE_COMPARE_AS(dst.attribute(Trade::MeshAttribute::Position, 0, 37), + Containers::arrayView({ + {3.0f, 1.0f}, + {6.0f, 4.0f}, + {9.0f, 7.0f}, + {0.0f, 2.0f}, + {5.0f, 8.0f}, + {2.0f, 0.0f}, + {}, {}, {}, /* Missing in the third mesh */ + }), TestSuite::Compare::Container); CORRADE_COMPARE_AS(dst.attribute(Trade::MeshAttribute::TextureCoordinates), Containers::arrayView({ {0.1f, 0.2f}, @@ -219,10 +277,10 @@ void ConcatenateTest::concatenate() { {0.525f, 0.575f}, {0.625f, 0.675f} }), TestSuite::Compare::Container); - CORRADE_COMPARE(dst.attributeName(3), Trade::meshAttributeCustom(42)); - CORRADE_COMPARE(dst.attributeFormat(3), VertexFormat::Short); - CORRADE_COMPARE(dst.attributeArraySize(3), 3); - CORRADE_COMPARE_AS((Containers::arrayCast<1, const Vector3s>(dst.attribute(3))), + CORRADE_COMPARE(dst.attributeName(4), Trade::meshAttributeCustom(42)); + CORRADE_COMPARE(dst.attributeFormat(4), VertexFormat::Short); + CORRADE_COMPARE(dst.attributeArraySize(4), 3); + CORRADE_COMPARE_AS((Containers::arrayCast<1, const Vector3s>(dst.attribute(4))), Containers::arrayView({ {15, 3, -1}, {14, 2, -4}, {28, -15, 0}, {29, -16, 1}, {30, -17, 2}, {40, -18, 3}, @@ -240,19 +298,23 @@ void ConcatenateTest::concatenate() { CORRADE_VERIFY(isInterleaved(dst)); if(data.shouldPreserveLayout) { - /* The original interleaved layout should be preserved */ + /* The original interleaved layout should be preserved, including the + order (attribute 1 is between 3 and 4) */ CORRADE_COMPARE(dst.attributeStride(0), sizeof(VertexDataA)); CORRADE_COMPARE(dst.attributeOffset(0), 0); - CORRADE_COMPARE(dst.attributeOffset(1), sizeof(Vector2)); - CORRADE_COMPARE(dst.attributeOffset(2), 2*sizeof(Vector2) + 4); - CORRADE_COMPARE(dst.attributeOffset(3), 2*sizeof(Vector2) + 4 + sizeof(Vector3)); + CORRADE_COMPARE(dst.attributeOffset(2), sizeof(Vector2)); + CORRADE_COMPARE(dst.attributeOffset(3), 2*sizeof(Vector2) + 4); + CORRADE_COMPARE(dst.attributeOffset(1), 2*sizeof(Vector2) + 4 + sizeof(Vector3)); + CORRADE_COMPARE(dst.attributeOffset(4), 3*sizeof(Vector2) + 4 + sizeof(Vector3)); } else { - /* Everything gets tightly packed */ - CORRADE_COMPARE(dst.attributeStride(0), 2*sizeof(Vector2) + sizeof(Vector3) + 3*sizeof(Short)); + /* Everything gets tightly packed in offsets following attribute + order */ + CORRADE_COMPARE(dst.attributeStride(0), 3*sizeof(Vector2) + sizeof(Vector3) + 3*sizeof(Short)); CORRADE_COMPARE(dst.attributeOffset(0), 0); CORRADE_COMPARE(dst.attributeOffset(1), sizeof(Vector2)); CORRADE_COMPARE(dst.attributeOffset(2), 2*sizeof(Vector2)); - CORRADE_COMPARE(dst.attributeOffset(3), 2*sizeof(Vector2) + sizeof(Vector3)); + CORRADE_COMPARE(dst.attributeOffset(3), 3*sizeof(Vector2)); + CORRADE_COMPARE(dst.attributeOffset(4), 3*sizeof(Vector2) + sizeof(Vector3)); } }