diff --git a/src/Magnum/MeshTools/Concatenate.cpp b/src/Magnum/MeshTools/Concatenate.cpp index f50596b5e..ff56ddb57 100644 --- a/src/Magnum/MeshTools/Concatenate.cpp +++ b/src/Magnum/MeshTools/Concatenate.cpp @@ -189,6 +189,14 @@ Trade::MeshData concatenate(const Containers::ArrayViewattributeCount(); ++i) { + const VertexFormat format = meshes.front()->attributeFormat(i); + CORRADE_ASSERT(!isVertexFormatImplementationSpecific(format), + "MeshTools::concatenate(): attribute" << i << "of the first mesh has an implementation-specific format" << reinterpret_cast(vertexFormatUnwrap(format)), + (Trade::MeshData{MeshPrimitive::Points, 0})); + } + #endif /* Calculate final attribute stride and offsets. Make a non-owning copy of the attribute data to avoid interleavedLayout() stealing the original diff --git a/src/Magnum/MeshTools/Concatenate.h b/src/Magnum/MeshTools/Concatenate.h index 4889db09d..af8167311 100644 --- a/src/Magnum/MeshTools/Concatenate.h +++ b/src/Magnum/MeshTools/Concatenate.h @@ -57,20 +57,21 @@ concatenated --- use @ref generateIndices() to turn them into @ref MeshPrimitive::Lines or @ref MeshPrimitive::Triangles first. The @p meshes array is expected to have at least one item. -All attributes from the first mesh are taken; for each following mesh -attributes present in the first are copied, superfluous attributes ignored and -missing attributes zeroed out. Matching attributes are expected to have the -same type, all meshes are expected to have the same primitive. The vertex data -are concatenated in the same order as passed, with no duplicate removal. -Returned instance vertex and index data flags always have both -@ref Trade::DataFlag::Owned and @ref Trade::DataFlag::Mutable to guarante -mutable access to particular parts of the concatenated mesh --- for example for -applying transformations. +All attributes from the first mesh are taken, expected to not have an +implementation-specific format. For each following mesh attributes present in +the first are copied, superfluous attributes ignored and missing attributes +zeroed out. Matching attributes are expected to have the same type, all meshes +are expected to have the same primitive. The vertex data are concatenated in +the same order as passed, with no duplicate removal. Returned instance vertex +and index data flags always have both @ref Trade::DataFlag::Owned and +@ref Trade::DataFlag::Mutable to guarante mutable access to particular parts of +the concatenated mesh --- for example for applying transformations. If an index buffer is needed, @ref MeshIndexType::UnsignedInt is always used. Call @ref compressIndices(const Trade::MeshData&, MeshIndexType) on the result to compress it to a smaller type, if desired. -@see @ref concatenateInto(), @ref SceneTools::flattenMeshHierarchy2D(), +@see @ref concatenateInto(), @ref isVertexFormatImplementationSpecific(), + @ref SceneTools::flattenMeshHierarchy2D(), @ref SceneTools::flattenMeshHierarchy3D() */ MAGNUM_MESHTOOLS_EXPORT Trade::MeshData concatenate(Containers::ArrayView> meshes); @@ -99,6 +100,13 @@ layout from @p destination is used, all vertex/index data are taken from template class Allocator = Containers::ArrayAllocator> void concatenateInto(Trade::MeshData& destination, const Containers::ArrayView> meshes) { CORRADE_ASSERT(!meshes.empty(), "MeshTools::concatenateInto(): no meshes passed", ); + #ifndef CORRADE_NO_ASSERT + for(std::size_t i = 0; i != destination.attributeCount(); ++i) { + const VertexFormat format = destination.attributeFormat(i); + CORRADE_ASSERT(!isVertexFormatImplementationSpecific(format), + "MeshTools::concatenateInto(): attribute" << i << "of the destination mesh has an implementation-specific format" << reinterpret_cast(vertexFormatUnwrap(format)), ); + } + #endif std::pair indexVertexCount = Implementation::concatenateIndexVertexCount(meshes); diff --git a/src/Magnum/MeshTools/Test/ConcatenateTest.cpp b/src/Magnum/MeshTools/Test/ConcatenateTest.cpp index d62204d01..cb60fcee0 100644 --- a/src/Magnum/MeshTools/Test/ConcatenateTest.cpp +++ b/src/Magnum/MeshTools/Test/ConcatenateTest.cpp @@ -50,6 +50,7 @@ struct ConcatenateTest: TestSuite::Tester { void concatenateInconsistentPrimitive(); void concatenateInconsistentAttributeFormat(); void concatenateInconsistentAttributeArraySize(); + void concatenateImplementationSpecificVertexFormat(); void concatenateIntoNoMeshes(); }; @@ -68,6 +69,7 @@ ConcatenateTest::ConcatenateTest() { &ConcatenateTest::concatenateInconsistentPrimitive, &ConcatenateTest::concatenateInconsistentAttributeFormat, &ConcatenateTest::concatenateInconsistentAttributeArraySize, + &ConcatenateTest::concatenateImplementationSpecificVertexFormat, &ConcatenateTest::concatenateIntoNoMeshes}); } @@ -637,6 +639,36 @@ void ConcatenateTest::concatenateInconsistentAttributeArraySize() { "MeshTools::concatenateInto(): expected array size 5 for attribute 2 (Trade::MeshAttribute::Custom(42)) but got 4 in mesh 3 attribute 1\n"); } +void ConcatenateTest::concatenateImplementationSpecificVertexFormat() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + /* Things are a bit duplicated to test correct numbering */ + Trade::MeshData a{MeshPrimitive::Lines, nullptr, { + Trade::MeshAttributeData{Trade::MeshAttribute::Position, + VertexFormat::Vector3, nullptr}, + Trade::MeshAttributeData{Trade::MeshAttribute::Normal, + VertexFormat::Vector3, nullptr}, + Trade::MeshAttributeData{Trade::MeshAttribute::Color, + vertexFormatWrap(0xcaca), nullptr} + }}; + Trade::MeshData b{MeshPrimitive::Lines, nullptr, { + Trade::MeshAttributeData{Trade::MeshAttribute::Position, + VertexFormat::Vector3, nullptr}, + Trade::MeshAttributeData{Trade::MeshAttribute::Normal, + VertexFormat::Vector3, nullptr} + }}; + + std::ostringstream out; + Error redirectError{&out}; + MeshTools::concatenate({a, b}); + MeshTools::concatenateInto(a, {b}); + CORRADE_COMPARE(out.str(), + "MeshTools::concatenate(): attribute 2 of the first mesh has an implementation-specific format 0xcaca\n" + "MeshTools::concatenateInto(): attribute 2 of the destination mesh has an implementation-specific format 0xcaca\n"); +} + void ConcatenateTest::concatenateIntoNoMeshes() { #ifdef CORRADE_NO_ASSERT CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");