diff --git a/doc/changelog.dox b/doc/changelog.dox index d23240bf6..6977eca3d 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -91,6 +91,7 @@ See also: @ref Trade::PhongMaterialData::normalTextureScale() and @ref Trade::PhongMaterialData::normalTextureSwizzle() to make new features added for PBR materials recognizable also in classic Phong workflows. +- New @ref Trade::SkinData class for skin import @subsection changelog-latest-changes Changes and improvements diff --git a/src/Magnum/Trade/CMakeLists.txt b/src/Magnum/Trade/CMakeLists.txt index d15b58eb8..61c7b821d 100644 --- a/src/Magnum/Trade/CMakeLists.txt +++ b/src/Magnum/Trade/CMakeLists.txt @@ -49,7 +49,8 @@ set(MagnumTrade_GracefulAssert_SRCS PbrClearCoatMaterialData.cpp PbrMetallicRoughnessMaterialData.cpp PbrSpecularGlossinessMaterialData.cpp - PhongMaterialData.cpp) + PhongMaterialData.cpp + SkinData.cpp) set(MagnumTrade_HEADERS AbstractImporter.h @@ -74,6 +75,7 @@ set(MagnumTrade_HEADERS PbrSpecularGlossinessMaterialData.h PhongMaterialData.h SceneData.h + SkinData.h TextureData.h Trade.h diff --git a/src/Magnum/Trade/SkinData.cpp b/src/Magnum/Trade/SkinData.cpp new file mode 100644 index 000000000..34d722f58 --- /dev/null +++ b/src/Magnum/Trade/SkinData.cpp @@ -0,0 +1,60 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 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 "SkinData.h" + +#include "Magnum/Math/Matrix3.h" +#include "Magnum/Math/Matrix4.h" +#include "Magnum/Trade/Data.h" + +namespace Magnum { namespace Trade { + +template SkinData::SkinData(Containers::Array&& jointData, Containers::Array>&& inverseBindMatrixData, const void* const importerState) noexcept: _jointData{std::move(jointData)}, _inverseBindMatrixData{std::move(inverseBindMatrixData)}, _importerState{importerState} { + CORRADE_ASSERT(_jointData.size() == _inverseBindMatrixData.size(), + "Trade::SkinData: joint and inverse bind matrix arrays have different size, got" << _jointData.size() << "and" << _inverseBindMatrixData.size(), ); +} + +template SkinData::SkinData(const std::initializer_list joints, const std::initializer_list> inverseBindMatrices, const void* const importerState): SkinData{Containers::array(joints), Containers::array(inverseBindMatrices), importerState} {} + +template SkinData::SkinData(DataFlags, const Containers::ArrayView jointData, DataFlags, const Containers::ArrayView> inverseBindMatrixData, const void* const importerState) noexcept: SkinData{Containers::Array{const_cast(jointData.data()), jointData.size(), reinterpret_cast(Implementation::nonOwnedArrayDeleter)}, Containers::Array>{const_cast*>(inverseBindMatrixData.data()), inverseBindMatrixData.size(), reinterpret_cast*, std::size_t)>(Implementation::nonOwnedArrayDeleter)}, importerState} {} + +template SkinData::SkinData(SkinData&&) noexcept = default; + +template SkinData& SkinData::operator=(SkinData&&) noexcept = default; + +template Containers::Array SkinData::releaseJointData() { + return std::move(_jointData); +} + +template Containers::Array> SkinData::releaseInverseBindMatrixData() { + return std::move(_inverseBindMatrixData); +} + +#ifndef DOXYGEN_GENERATING_OUTPUT +template class MAGNUM_TRADE_EXPORT SkinData<2>; +template class MAGNUM_TRADE_EXPORT SkinData<3>; +#endif + +}} diff --git a/src/Magnum/Trade/SkinData.h b/src/Magnum/Trade/SkinData.h new file mode 100644 index 000000000..232e6d235 --- /dev/null +++ b/src/Magnum/Trade/SkinData.h @@ -0,0 +1,154 @@ +#ifndef Magnum_Trade_SkinData_h +#define Magnum_Trade_SkinData_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 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. +*/ + +/** @file + * @brief Class @ref Magnum::Trade::SkinData + * @m_since_latest + */ + +#include + +#include "Magnum/DimensionTraits.h" +#include "Magnum/Trade/Trade.h" +#include "Magnum/Trade/visibility.h" + +namespace Magnum { namespace Trade { + +/** +@brief Skin data +@m_since_latest + +@see @ref SkinData2D, @ref SkinData3D, @ref AbstractImporter::skin2D(), + @ref AbstractImporter::skin3D() +*/ +template class SkinData { + public: + /** + * @brief Constructor + * @param jointData IDs of objects that act as joints + * @param inverseBindMatrixData Inverse bind matrix for each joint + * @param importerState Importer-specific state + * + * The @p jointData and @p inverseBindMatrixData arrays are expected to + * have the same size. + */ + explicit SkinData(Containers::Array&& jointData, Containers::Array>&& inverseBindMatrixData, const void* importerState = nullptr) noexcept; + + /** @overload */ + explicit SkinData(std::initializer_list joints, std::initializer_list> inverseBindMatrices, const void* importerState = nullptr); + + /** + * @brief Construct a non-owned skin data + * @param jointDataFlags Ignored. Used only for a safer distinction + * from the owning constructor. + * @param jointData IDs of objects that act as joints + * @param inverseBindMatrixDataFlags Ignored. Used only for a safer + * distinction from the owning constructor. + * @param inverseBindMatrixData Inverse bind matrix for each joint + * @param importerState Importer-specific state + * + * The @p jointData and @p inverseBindMatrixData arrays are expected to + * have the same size. + */ + explicit SkinData(DataFlags jointDataFlags, Containers::ArrayView jointData, DataFlags inverseBindMatrixDataFlags, Containers::ArrayView> inverseBindMatrixData, const void* importerState = nullptr) noexcept; + + /** @brief Copying is not allowed */ + SkinData(const SkinData&) = delete; + + /** @brief Move constructor */ + SkinData(SkinData&& other) noexcept; + + /** @brief Copying is not allowed */ + SkinData& operator=(const SkinData&) = delete; + + /** @brief Move assignment */ + SkinData& operator=(SkinData&& other) noexcept; + + /** + * @brief Joint IDs + * + * IDs of objects that act as joints. + * @see @ref AbstractImporter::object2D(), + * @ref AbstractImporter::object3D(), @ref releaseJointData() + */ + Containers::ArrayView joints() const { return _jointData; } + + /** + * @brief Inverse bind matrices + * + * Transforms each joint to the initial state for skinning to be + * applied. The returned array has the same size as @ref joints(). + */ + Containers::ArrayView> inverseBindMatrices() const { return _inverseBindMatrixData; } + + /** + * @brief Release joint data storage + * + * Releases the ownership of the joint ID array. The material then + * behaves like if it has no joints. + * @see @ref joints() + */ + Containers::Array releaseJointData(); + + /** + * @brief Release inverse bind matrix data storage + * + * Releases the ownership of the inverse bind matrix array. The + * material then behaves like if it has no matrices. + * @see @ref inverseBindMatrices() + */ + Containers::Array> releaseInverseBindMatrixData(); + + /** @brief Importer-specific state */ + const void* importerState() const { return _importerState; } + + private: + /** @todo skeleton object ID? gltf has that but the use is unclear */ + Containers::Array _jointData; + Containers::Array> _inverseBindMatrixData; + const void* _importerState; +}; + +/** +@brief Two-dimensional skin data +@m_since_latest + +@see @ref AbstractImporter::skin2D() +*/ +typedef SkinData<2> SkinData2D; + +/** +@brief Three-dimensional skin data +@m_since_latest + +@see @ref AbstractImporter::skin3D() +*/ +typedef SkinData<3> SkinData3D; + +}} + +#endif diff --git a/src/Magnum/Trade/Test/CMakeLists.txt b/src/Magnum/Trade/Test/CMakeLists.txt index d04cd597b..a4ae7ecfa 100644 --- a/src/Magnum/Trade/Test/CMakeLists.txt +++ b/src/Magnum/Trade/Test/CMakeLists.txt @@ -56,6 +56,7 @@ corrade_add_test(TradeMeshDataTest MeshDataTest.cpp LIBRARIES MagnumTradeTestLib corrade_add_test(TradeObjectData2DTest ObjectData2DTest.cpp LIBRARIES MagnumTradeTestLib) corrade_add_test(TradeObjectData3DTest ObjectData3DTest.cpp LIBRARIES MagnumTradeTestLib) corrade_add_test(TradeSceneDataTest SceneDataTest.cpp LIBRARIES MagnumTrade) +corrade_add_test(TradeSkinDataTest SkinDataTest.cpp LIBRARIES MagnumTradeTestLib) corrade_add_test(TradeTextureDataTest TextureDataTest.cpp LIBRARIES MagnumTrade) set_property(TARGET diff --git a/src/Magnum/Trade/Test/SkinDataTest.cpp b/src/Magnum/Trade/Test/SkinDataTest.cpp new file mode 100644 index 000000000..0e0d28f2f --- /dev/null +++ b/src/Magnum/Trade/Test/SkinDataTest.cpp @@ -0,0 +1,155 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, + 2020 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 +#include +#include + +#include "Magnum/Math/Matrix3.h" +#include "Magnum/Math/Matrix4.h" +#include "Magnum/Trade/SkinData.h" + +namespace Magnum { namespace Trade { namespace Test { namespace { + +struct SkinDataTest: TestSuite::Tester { + explicit SkinDataTest(); + + void construct(); + void constructNonOwned(); + void constructDifferentSize(); + void constructCopy(); + void constructMove(); + + void release(); +}; + +SkinDataTest::SkinDataTest() { + addTests({&SkinDataTest::construct, + &SkinDataTest::constructNonOwned, + &SkinDataTest::constructDifferentSize, + &SkinDataTest::constructCopy, + &SkinDataTest::constructMove, + + &SkinDataTest::release}); +} + +void SkinDataTest::construct() { + int state; + SkinData3D data{{0, 2, 3}, { + Matrix4::translation(Vector3::zAxis(0.0f)), + Matrix4::translation(Vector3::zAxis(2.0f)), + Matrix4::translation(Vector3::zAxis(4.0f)), + }, &state}; + + CORRADE_COMPARE(data.joints()[1], 2); + CORRADE_COMPARE(data.inverseBindMatrices()[1], Matrix4::translation(Vector3::zAxis(2.0f))); + CORRADE_COMPARE(data.importerState(), &state); +} + +void SkinDataTest::constructNonOwned() { + int state; + const UnsignedInt jointData[]{0, 2, 3}; + const Matrix4 inverseBindMatrixData[]{ + Matrix4::translation(Vector3::zAxis(0.0f)), + Matrix4::translation(Vector3::zAxis(2.0f)), + Matrix4::translation(Vector3::zAxis(4.0f)) + }; + SkinData3D data{{}, jointData, {}, inverseBindMatrixData, &state}; + + CORRADE_COMPARE(data.joints().size(), 3); + CORRADE_COMPARE(data.joints().data(), jointData); + CORRADE_COMPARE(data.inverseBindMatrices().size(), 3); + CORRADE_COMPARE(data.inverseBindMatrices().data(), inverseBindMatrixData); + CORRADE_COMPARE(data.importerState(), &state); +} + +void SkinDataTest::constructDifferentSize() { + #ifdef CORRADE_NO_ASSERT + CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); + #endif + + std::ostringstream out; + Error redirectError{&out}; + SkinData3D data{{0, 2}, {{}, {}, {}}}; + CORRADE_COMPARE(out.str(), "Trade::SkinData: joint and inverse bind matrix arrays have different size, got 2 and 3\n"); +} + +void SkinDataTest::constructCopy() { + CORRADE_VERIFY(!(std::is_constructible{})); + CORRADE_VERIFY(!(std::is_assignable{})); +} + +void SkinDataTest::constructMove() { + int state; + SkinData3D a{{0, 2, 3}, { + Matrix4::translation(Vector3::zAxis(0.0f)), + Matrix4::translation(Vector3::zAxis(2.0f)), + Matrix4::translation(Vector3::zAxis(4.0f)), + }, &state}; + + SkinData3D b = std::move(a); + CORRADE_COMPARE(b.joints()[1], 2); + CORRADE_COMPARE(b.inverseBindMatrices()[1], Matrix4::translation(Vector3::zAxis(2.0f))); + CORRADE_COMPARE(b.importerState(), &state); + + SkinData3D c{{}, {}}; + c = std::move(b); + CORRADE_COMPARE(c.joints()[1], 2); + CORRADE_COMPARE(c.inverseBindMatrices()[1], Matrix4::translation(Vector3::zAxis(2.0f))); + CORRADE_COMPARE(c.importerState(), &state); + + CORRADE_VERIFY(std::is_nothrow_move_constructible::value); + CORRADE_VERIFY(std::is_nothrow_move_assignable::value); +} + +void SkinDataTest::release() { + Containers::Array joints{Containers::InPlaceInit, {0, 2, 3}}; + Containers::Array inverseBindMatrices{Containers::InPlaceInit, { + Matrix3::translation(Vector2::yAxis(0.0f)), + Matrix3::translation(Vector2::yAxis(2.0f)), + Matrix3::translation(Vector2::yAxis(4.0f)) + }}; + + const void* jointsPointer = joints; + const void* inverseBindMatricesPointer = inverseBindMatrices; + + SkinData2D data{std::move(joints), std::move(inverseBindMatrices)}; + + Containers::Array releasedJoints = data.releaseJointData(); + CORRADE_COMPARE(data.joints().size(), 0); + CORRADE_COMPARE(data.inverseBindMatrices().size(), 3); + CORRADE_COMPARE(releasedJoints.size(), 3); + CORRADE_COMPARE(releasedJoints, jointsPointer); + + Containers::Array releasedInverseBindMatrices = data.releaseInverseBindMatrixData(); + CORRADE_COMPARE(data.joints().size(), 0); + CORRADE_COMPARE(data.inverseBindMatrices().size(), 0); + CORRADE_COMPARE(releasedInverseBindMatrices.size(), 3); + CORRADE_COMPARE(releasedInverseBindMatrices, inverseBindMatricesPointer); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::Trade::Test::SkinDataTest) diff --git a/src/Magnum/Trade/Trade.h b/src/Magnum/Trade/Trade.h index dac3bb738..7ec5d544b 100644 --- a/src/Magnum/Trade/Trade.h +++ b/src/Magnum/Trade/Trade.h @@ -99,6 +99,10 @@ class PbrSpecularGlossinessMaterialData; class PhongMaterialData; class TextureData; class SceneData; + +template class SkinData; +typedef SkinData<2> SkinData2D; +typedef SkinData<3> SkinData3D; #endif }}