From 56299bed374e8f2c0b51b032ecefe84d8beb9724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 28 Feb 2012 10:14:56 +0100 Subject: [PATCH] Added tool for combining index arrays. --- src/MeshTools/CombineIndexedArrays.h | 141 ++++++++++++++++++ src/MeshTools/Test/CMakeLists.txt | 1 + .../Test/CombineIndexedArraysTest.cpp | 61 ++++++++ src/MeshTools/Test/CombineIndexedArraysTest.h | 32 ++++ 4 files changed, 235 insertions(+) create mode 100644 src/MeshTools/CombineIndexedArrays.h create mode 100644 src/MeshTools/Test/CombineIndexedArraysTest.cpp create mode 100644 src/MeshTools/Test/CombineIndexedArraysTest.h diff --git a/src/MeshTools/CombineIndexedArrays.h b/src/MeshTools/CombineIndexedArrays.h new file mode 100644 index 000000000..069102efa --- /dev/null +++ b/src/MeshTools/CombineIndexedArrays.h @@ -0,0 +1,141 @@ +#ifndef Magnum_MeshTools_CombineIndexedArrays_h +#define Magnum_MeshTools_CombineIndexedArrays_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Function Magnum::MeshTools::combineIndexedArrays() + */ + +#include +#include +#include +#include + +#include "Math/Vector.h" +#include "MeshTools/Clean.h" + +namespace Magnum { namespace MeshTools { + +#ifndef DOXYGEN_GENERATING_OUTPUT +namespace Implementation { + +class CombineIndexedArrays { + public: + template std::vector operator()(const std::tuple&, std::vector&>&... indexedArrays) { + /* Compute index count */ + size_t _indexCount = indexCount(std::get<0>(indexedArrays)...); + + /* Resulting index array */ + std::vector result; + result.resize(_indexCount); + std::iota(result.begin(), result.end(), 0); + + /* All index combinations */ + std::vector > indexCombinations(_indexCount); + writeCombinedIndices(indexCombinations, std::get<0>(indexedArrays)...); + + /* Make the combinations unique */ + MeshTools::clean(result, indexCombinations); + + /* Write combined arrays */ + writeCombinedArrays(indexCombinations, std::get<1>(indexedArrays)...); + + return result; + } + + private: + template inline static size_t indexCount(const std::vector& first, const std::vector&... next) { + size_t count = indexCount(next...); + if(sizeof...(next) != 0 && count != first.size()) { + Corrade::Utility::Error() << "MeshTools::combineIndexedArrays(): index arrays don't have the same length, nothing done."; + assert(0); + return 0; + } + return first.size(); + } + + template static void writeCombinedIndices(std::vector>& output, const std::vector& first, const std::vector&... next) { + /* Copy the data to output */ + for(size_t i = 0; i != output.size(); ++i) + output[i][size-sizeof...(next)-1] = first[i]; + + writeCombinedIndices(output, next...); + } + + template static void writeCombinedArrays(const std::vector>& combinedIndices, std::vector& first, std::vector&... next) { + /* Rewrite output array */ + std::vector output; + for(size_t i = 0; i != combinedIndices.size(); ++i) + output.push_back(first[combinedIndices[i][size-sizeof...(next)-1]]); + std::swap(output, first); + + writeCombinedArrays(combinedIndices, next...); + } + + /* Terminator functions for recursive calls */ + inline static size_t indexCount() { return 0; } + template inline static void writeCombinedIndices(std::vector>&) {} + template inline static void writeCombinedArrays(const std::vector>&) {} +}; + +} +#endif + +/** +@brief Combine indexed arrays +@param indexedArrays Index and attribute arrays +@return Array with resulting indices + +When you have e.g. vertex, normal and texture array, each indexed with +different indices, you can use this function to combine them to use the same +indices. The function returns array with resulting indices and replaces +original attribute arrays with combined ones. + +The index array must be passed as const reference (to avoid copying) and +attribute array as reference, so it can be replaced with combined data. To +avoid explicit verbose specification of tuple type, you can write it with help +of some STL functions like shown below. Also if one index array is shader by +more than one attribute array, just pass the index array more times. Example: +@code +std::vector vertexIndices; +std::vector vertices; +std::vector normalTextureIndices; +std::vector normals; +std::vector textureCoordinates; + +std::vector indices = MeshTools::combineIndexedArrays( + std::make_tuple(std::cref(vertexIndices), std::ref(vertices)), + std::make_tuple(std::cref(normaTextureIndices), std::ref(normals)), + std::make_tuple(std::cref(normaTextureIndices), std::ref(textureCoordinates)) +); +@endcode +`vertices`, `normals` and `textureCoordinates` will then contain combined +attributes indexed with `indices`. + +@attention All index arrays should have the same size, otherwise zero-length + output is generated. + +@internal Implementation note: It's done using tuples because it is more clear + which parameter is index array and which is attribute array, mainly when + both are of the same type. +*/ +template std::vector combineIndexedArrays(const std::tuple&, std::vector&>&... indexedArrays) { + return Implementation::CombineIndexedArrays()(indexedArrays...); +} + +}} + +#endif diff --git a/src/MeshTools/Test/CMakeLists.txt b/src/MeshTools/Test/CMakeLists.txt index 230264038..9dd4c453d 100644 --- a/src/MeshTools/Test/CMakeLists.txt +++ b/src/MeshTools/Test/CMakeLists.txt @@ -1,4 +1,5 @@ corrade_add_test(CleanTest CleanTest.h CleanTest.cpp ${CORRADE_UTILITY_LIBRARY}) +corrade_add_test(CombineIndexedArraysTest CombineIndexedArraysTest.h CombineIndexedArraysTest.cpp ${CORRADE_UTILITY_LIBRARY}) corrade_add_test(CompressIndicesTest CompressIndicesTest.h CompressIndicesTest.cpp ${CORRADE_UTILITY_LIBRARY} ${MAGNUM_LIBRARY}) corrade_add_test(InterleaveTest InterleaveTest.h InterleaveTest.cpp ${CORRADE_UTILITY_LIBRARY}) corrade_add_test(SubdivideTest SubdivideTest.h SubdivideTest.cpp ${CORRADE_UTILITY_LIBRARY}) diff --git a/src/MeshTools/Test/CombineIndexedArraysTest.cpp b/src/MeshTools/Test/CombineIndexedArraysTest.cpp new file mode 100644 index 000000000..c79405892 --- /dev/null +++ b/src/MeshTools/Test/CombineIndexedArraysTest.cpp @@ -0,0 +1,61 @@ +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#include "CombineIndexedArraysTest.h" + +#define NDEBUG + +#include +#include + +#include "MeshTools/CombineIndexedArrays.h" + +QTEST_APPLESS_MAIN(Magnum::MeshTools::Test::CombineIndexedArraysTest) + +using namespace std; +using namespace Corrade::Utility; + +namespace Magnum { namespace MeshTools { namespace Test { + +void CombineIndexedArraysTest::wrongIndexCount() { + stringstream ss; + Error::setOutput(&ss); + vector array; + vector result = MeshTools::combineIndexedArrays( + tuple&, vector&>(vector{0, 1, 0}, array), + tuple&, vector&>(vector{3, 4}, array)); + + QVERIFY(result.size() == 0); + QVERIFY(ss.str() == "MeshTools::combineIndexedArrays(): index arrays don't have the same length, nothing done.\n"); +} + +void CombineIndexedArraysTest::combine() { + vector array1{ 0, 1 }; + vector array2{ 0, 1, 2, 3, 4 }; + vector array3{ 0, 1, 2, 3, 4, 5, 6, 7 }; + + vector result = MeshTools::combineIndexedArrays( + tuple&, vector&>(vector{0, 1, 0}, array1), + tuple&, vector&>(vector{3, 4, 3}, array2), + tuple&, vector&>(vector{6, 7, 6}, array3)); + + QVERIFY((result == vector{0, 1, 0})); + QVERIFY((array1 == vector{0, 1})); + QVERIFY((array2 == vector{3, 4})); + QVERIFY((array3 == vector{6, 7})); +} + + +}}} diff --git a/src/MeshTools/Test/CombineIndexedArraysTest.h b/src/MeshTools/Test/CombineIndexedArraysTest.h new file mode 100644 index 000000000..cb210290c --- /dev/null +++ b/src/MeshTools/Test/CombineIndexedArraysTest.h @@ -0,0 +1,32 @@ +#ifndef Magnum_MeshTools_Test_CombineIndexedArraysTest_h +#define Magnum_MeshTools_Test_CombineIndexedArraysTest_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +#include + +namespace Magnum { namespace MeshTools { namespace Test { + +class CombineIndexedArraysTest: public QObject { + Q_OBJECT + + private slots: + void wrongIndexCount(); + void combine(); +}; + +}}} + +#endif