diff --git a/doc/changelog.dox b/doc/changelog.dox index 0e293d4b0..80c3892ec 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -109,6 +109,8 @@ See also: @subsubsection changelog-latest-new-meshtools MeshTools library +- New @ref MeshTools::isInterleaved() utility for checking if + @ref Trade::MeshData is interleaved - Added @ref MeshTools::subdivideInPlace() for allocation-less mesh subdivision - New @ref MeshTools::removeDuplicatesInPlace() variant that works on diff --git a/src/Magnum/MeshTools/CMakeLists.txt b/src/Magnum/MeshTools/CMakeLists.txt index a1a7c4d47..51d3b4727 100644 --- a/src/Magnum/MeshTools/CMakeLists.txt +++ b/src/Magnum/MeshTools/CMakeLists.txt @@ -25,6 +25,7 @@ # Files shared between main library and unit test library set(MagnumMeshTools_SRCS + Interleave.cpp Tipsify.cpp) # Files compiled with different flags for main library and unit test library diff --git a/src/Magnum/MeshTools/Interleave.cpp b/src/Magnum/MeshTools/Interleave.cpp new file mode 100644 index 000000000..0a6d4d150 --- /dev/null +++ b/src/Magnum/MeshTools/Interleave.cpp @@ -0,0 +1,52 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 + Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "Interleave.h" + +#include "Magnum/Math/Functions.h" +#include "Magnum/Trade/MeshData.h" + +namespace Magnum { namespace MeshTools { + +bool isInterleaved(const Trade::MeshData& data) { + /* There is nothing, so yes it is (because there is nothing we could do + to make it interleaved anyway) */ + if(!data.attributeCount()) return true; + + const UnsignedInt stride = data.attributeStride(0); + std::size_t minOffset = data.attributeOffset(0); + std::size_t maxOffset = minOffset; + for(UnsignedInt i = 1; i != data.attributeCount(); ++i) { + if(data.attributeStride(i) != stride) return false; + + const std::size_t offset = data.attributeOffset(i); + minOffset = Math::min(minOffset, offset); + maxOffset = Math::max(maxOffset, offset + vertexFormatSize(data.attributeFormat(i))); + } + + return maxOffset - minOffset <= stride; +} + +}} diff --git a/src/Magnum/MeshTools/Interleave.h b/src/Magnum/MeshTools/Interleave.h index c95b19afd..623c4345b 100644 --- a/src/Magnum/MeshTools/Interleave.h +++ b/src/Magnum/MeshTools/Interleave.h @@ -26,7 +26,7 @@ */ /** @file - * @brief Function @ref Magnum::MeshTools::interleave(), @ref Magnum::MeshTools::interleaveInto() + * @brief Function @ref Magnum::MeshTools::interleave(), @ref Magnum::MeshTools::interleaveInto(), @ref Magnum::MeshTools::isInterleaved() */ #include @@ -35,6 +35,8 @@ #include #include "Magnum/Magnum.h" +#include "Magnum/MeshTools/visibility.h" +#include "Magnum/Trade/Trade.h" namespace Magnum { namespace MeshTools { @@ -184,6 +186,19 @@ template void interleaveInto(Containers::ArrayView bu Implementation::writeInterleaved(stride, buffer.begin(), first, next...); } +/** +@brief If the mesh data is interleaved +@m_since_latest + +Returns @cpp true @ce if all attributes have the same stride and the difference +between minimal and maximal offset is not larger than the stride, @cpp false @ce +otherwise. In particular, returns @cpp true @ce also if the mesh has just one +or no attributes. +@see @ref Trade::MeshData::attributeStride(), + @ref Trade::MeshData::attributeOffset() +*/ +MAGNUM_MESHTOOLS_EXPORT bool isInterleaved(const Trade::MeshData& data); + }} #endif diff --git a/src/Magnum/MeshTools/Test/CMakeLists.txt b/src/Magnum/MeshTools/Test/CMakeLists.txt index 8c49708bd..aa15700bc 100644 --- a/src/Magnum/MeshTools/Test/CMakeLists.txt +++ b/src/Magnum/MeshTools/Test/CMakeLists.txt @@ -28,7 +28,7 @@ corrade_add_test(MeshToolsCompressIndicesTest CompressIndicesTest.cpp LIBRARIES corrade_add_test(MeshToolsDuplicateTest DuplicateTest.cpp LIBRARIES MagnumMeshToolsTestLib) corrade_add_test(MeshToolsFlipNormalsTest FlipNormalsTest.cpp LIBRARIES MagnumMeshToolsTestLib) corrade_add_test(MeshToolsGenerateNormalsTest GenerateNormalsTest.cpp LIBRARIES MagnumMeshToolsTestLib MagnumPrimitives) -corrade_add_test(MeshToolsInterleaveTest InterleaveTest.cpp LIBRARIES Magnum) +corrade_add_test(MeshToolsInterleaveTest InterleaveTest.cpp LIBRARIES MagnumMeshTools) corrade_add_test(MeshToolsRemoveDuplicatesTest RemoveDuplicatesTest.cpp LIBRARIES MagnumMeshToolsTestLib) corrade_add_test(MeshToolsSubdivideTest SubdivideTest.cpp LIBRARIES Magnum) corrade_add_test(MeshToolsTipsifyTest TipsifyTest.cpp LIBRARIES MagnumMeshTools) diff --git a/src/Magnum/MeshTools/Test/InterleaveTest.cpp b/src/Magnum/MeshTools/Test/InterleaveTest.cpp index 89de1aee8..8cb7267d6 100644 --- a/src/Magnum/MeshTools/Test/InterleaveTest.cpp +++ b/src/Magnum/MeshTools/Test/InterleaveTest.cpp @@ -30,7 +30,9 @@ #include #include +#include "Magnum/Math/Vector3.h" #include "Magnum/MeshTools/Interleave.h" +#include "Magnum/Trade/MeshData.h" namespace Magnum { namespace MeshTools { namespace Test { namespace { @@ -45,6 +47,14 @@ struct InterleaveTest: Corrade::TestSuite::Tester { void writeGaps(); void interleaveInto(); + + void isInterleaved(); + void isInterleavedEmpty(); + void isInterleavedSingleAttribute(); + void isInterleavedGaps(); + void isInterleavedAliased(); + void isInterleavedUnordered(); + void isInterleavedAttributeAcrossStride(); }; InterleaveTest::InterleaveTest() { @@ -55,7 +65,15 @@ InterleaveTest::InterleaveTest() { &InterleaveTest::write, &InterleaveTest::writeGaps, - &InterleaveTest::interleaveInto}); + &InterleaveTest::interleaveInto, + + &InterleaveTest::isInterleaved, + &InterleaveTest::isInterleavedEmpty, + &InterleaveTest::isInterleavedSingleAttribute, + &InterleaveTest::isInterleavedGaps, + &InterleaveTest::isInterleavedAliased, + &InterleaveTest::isInterleavedUnordered, + &InterleaveTest::isInterleavedAttributeAcrossStride}); } void InterleaveTest::attributeCount() { @@ -159,6 +177,114 @@ void InterleaveTest::interleaveInto() { } } +void InterleaveTest::isInterleaved() { + /* Interleaved; testing also initial offset */ + { + Containers::Array vertexData{100 + 3*20}; + Trade::MeshAttributeData positions{Trade::MeshAttribute::Position, + Containers::StridedArrayView1D{vertexData, + reinterpret_cast(vertexData.data() + 100), 3, 20}}; + Trade::MeshAttributeData normals{Trade::MeshAttribute::Normal, + Containers::StridedArrayView1D{vertexData, + reinterpret_cast(vertexData.data() + 100 + 8), 3, 20}}; + + Trade::MeshData data{MeshPrimitive::Triangles, std::move(vertexData), {positions, normals}}; + CORRADE_VERIFY(MeshTools::isInterleaved(data)); + } + + /* One after another */ + { + Containers::Array vertexData{100 + 3*20}; + Trade::MeshAttributeData positions{Trade::MeshAttribute::Position, + Containers::arrayCast(vertexData.suffix(100).prefix(3*8))}; + Trade::MeshAttributeData normals{Trade::MeshAttribute::Normal, + Containers::arrayCast(vertexData.suffix(100).suffix(3*8))}; + + Trade::MeshData data{MeshPrimitive::Triangles, std::move(vertexData), {positions, normals}}; + CORRADE_VERIFY(!MeshTools::isInterleaved(data)); + } +} + +void InterleaveTest::isInterleavedEmpty() { + Trade::MeshData data{MeshPrimitive::Triangles, 5}; + CORRADE_VERIFY(MeshTools::isInterleaved(data)); +} + +void InterleaveTest::isInterleavedSingleAttribute() { + Containers::Array vertexData{3*8}; + Trade::MeshAttributeData positions{Trade::MeshAttribute::Position, + Containers::arrayCast(vertexData.prefix(3*8))}; + + Trade::MeshData data{MeshPrimitive::Triangles, std::move(vertexData), {positions}}; + CORRADE_VERIFY(MeshTools::isInterleaved(data)); +} + +void InterleaveTest::isInterleavedGaps() { + Containers::Array vertexData{3*40}; + Trade::MeshAttributeData positions{Trade::MeshAttribute::Position, + Containers::StridedArrayView1D{vertexData, + reinterpret_cast(vertexData.data() + 5), 3, 40}}; + Trade::MeshAttributeData normals{Trade::MeshAttribute::Normal, + Containers::StridedArrayView1D{vertexData, + reinterpret_cast(vertexData.data() + 24), 3, 40}}; + + Trade::MeshData data{MeshPrimitive::Triangles, std::move(vertexData), {positions, normals}}; + CORRADE_VERIFY(MeshTools::isInterleaved(data)); +} + +void InterleaveTest::isInterleavedAliased() { + /* Normals share first two components with positions */ + Containers::Array vertexData{3*12}; + Trade::MeshAttributeData positions{Trade::MeshAttribute::Position, + Containers::StridedArrayView1D{vertexData, + reinterpret_cast(vertexData.data()), 3, 12}}; + Trade::MeshAttributeData normals{Trade::MeshAttribute::Normal, + Containers::StridedArrayView1D{vertexData, + reinterpret_cast(vertexData.data()), 3, 12}}; + + Trade::MeshData data{MeshPrimitive::Triangles, std::move(vertexData), {positions, normals}}; + CORRADE_VERIFY(MeshTools::isInterleaved(data)); +} + +void InterleaveTest::isInterleavedUnordered() { + Containers::Array vertexData{3*12}; + Trade::MeshAttributeData positions{Trade::MeshAttribute::Position, + Containers::StridedArrayView1D{vertexData, + reinterpret_cast(vertexData.data()), 3, 12}}; + Trade::MeshAttributeData normals{Trade::MeshAttribute::Normal, + Containers::StridedArrayView1D{vertexData, + reinterpret_cast(vertexData.data()), 3, 12}}; + + /* Normals specified first even though they're ordered after positions */ + Trade::MeshData data{MeshPrimitive::Triangles, std::move(vertexData), {normals, positions}}; + CORRADE_VERIFY(MeshTools::isInterleaved(data)); +} + +void InterleaveTest::isInterleavedAttributeAcrossStride() { + /* Data slightly larger */ + Containers::Array vertexData{5 + 3*30 + 3}; + Trade::MeshAttributeData positions{Trade::MeshAttribute::Position, + Containers::StridedArrayView1D{vertexData, + reinterpret_cast(vertexData.data() + 5), 3, 30}}; + Trade::MeshAttributeData normals{Trade::MeshAttribute::Normal, + Containers::StridedArrayView1D{vertexData, + /* 23 + 12 is 35, which still fits into the stride after + subtracting the initial offset; 24 not */ + reinterpret_cast(vertexData.data() + 23), 3, 30}}; + + Trade::MeshData data{MeshPrimitive::Triangles, std::move(vertexData), + {positions, normals}}; + CORRADE_VERIFY(MeshTools::isInterleaved(data)); + + vertexData = data.releaseVertexData(); + Trade::MeshAttributeData normals2{Trade::MeshAttribute::Normal, + Containers::StridedArrayView1D{vertexData, + reinterpret_cast(vertexData.data() + 24), 3, 30}}; + Trade::MeshData data2{MeshPrimitive::Triangles, + std::move(vertexData), {positions, normals2}}; + CORRADE_VERIFY(!MeshTools::isInterleaved(data2)); +} + }}}} CORRADE_TEST_MAIN(Magnum::MeshTools::Test::InterleaveTest)