Browse Source

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.
pull/623/head
Vladimír Vondruš 3 years ago
parent
commit
d982ba9ec4
  1. 14
      src/Magnum/MeshTools/Concatenate.cpp
  2. 110
      src/Magnum/MeshTools/Test/ConcatenateTest.cpp

14
src/Magnum/MeshTools/Concatenate.cpp

@ -123,14 +123,14 @@ Trade::MeshData concatenate(Containers::Array<char>&& 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<UnsignedInt> dst = out.findAttributeId(mesh.attributeName(src), mesh.attributeId(src));
const Containers::Optional<UnsignedInt> dst = out.findAttributeId(mesh.attributeName(src), mesh.attributeId(src), mesh.attributeMorphTargetId(src));
if(!dst)
continue;

110
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<const VertexDataA> 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<const VertexDataB> 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<const VertexDataC> 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<Vector3>(Trade::MeshAttribute::Position),
Containers::arrayView<Vector3>({
{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<Vector2>(Trade::MeshAttribute::Position, 0, 37),
Containers::arrayView<Vector2>({
{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<Vector2>(Trade::MeshAttribute::TextureCoordinates),
Containers::arrayView<Vector2>({
{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<Short[]>(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<Short[]>(4))),
Containers::arrayView<Vector3s>({
{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));
}
}

Loading…
Cancel
Save