From e056ad3c99452bcede5b6b3abf2cf5e9140edca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 1 Nov 2021 20:34:35 +0100 Subject: [PATCH] Trade: allow SceneField::Transformation to be a 3x2 or a 4x3 matrix. Another thought I had was about allowing a 2x2 / 3x3 matrix to be used for rotation, but there's the ambiguity with it possibly containing scaling / shear / whatever, which would then cause EXPLODING HEADACHES when converting to quaternions / complex numbers. --- src/Magnum/Trade/SceneData.cpp | 24 +++++++++ src/Magnum/Trade/SceneData.h | 10 ++++ src/Magnum/Trade/Test/SceneDataTest.cpp | 68 +++++++++++++++++++------ 3 files changed, 87 insertions(+), 15 deletions(-) diff --git a/src/Magnum/Trade/SceneData.cpp b/src/Magnum/Trade/SceneData.cpp index 46412f45b..44b9c685b 100644 --- a/src/Magnum/Trade/SceneData.cpp +++ b/src/Magnum/Trade/SceneData.cpp @@ -644,11 +644,15 @@ SceneData::SceneData(const SceneObjectType objectType, const UnsignedLong object const SceneFieldType fieldType = _fields[transformationField]._fieldType; if(fieldType == SceneFieldType::Matrix3x3 || fieldType == SceneFieldType::Matrix3x3d || + fieldType == SceneFieldType::Matrix3x2 || + fieldType == SceneFieldType::Matrix3x2d || fieldType == SceneFieldType::DualComplex || fieldType == SceneFieldType::DualComplexd) _dimensions = 2; else if(fieldType == SceneFieldType::Matrix4x4 || fieldType == SceneFieldType::Matrix4x4d || + fieldType == SceneFieldType::Matrix4x3 || + fieldType == SceneFieldType::Matrix4x3d || fieldType == SceneFieldType::DualQuaternion || fieldType == SceneFieldType::DualQuaterniond) _dimensions = 3; @@ -1200,6 +1204,18 @@ template void convertTransformation(const Conta destination[i] = Destination{sourceT[i].toMatrix()}; } +/* Expands 2x3 to 3x3, 4x3 to 4x4, while potentially also converting floats to + doubles */ +/** @todo also have some optimized batched API for this? doing a strided copy + and a fill seemed silly as that would cause the whole memory go through + cache twice */ +template void expandTransformationMatrix(const Containers::StridedArrayView1D& source, const Containers::StridedArrayView1D& destination) { + CORRADE_INTERNAL_ASSERT(source.size() == destination.size()); + const auto sourceT = Containers::arrayCast(source); + for(std::size_t i = 0; i != sourceT.size(); ++i) + destination[i] = Destination{Math::RectangularMatrix{sourceT[i]}}; +} + /** @todo these (or the float variants at least) should eventually be replaced with optimized batched APIs (applyTranslationsInto() updating just the last matrix column etc.) */ @@ -1299,6 +1315,10 @@ void SceneData::transformations2DIntoInternal(const UnsignedInt transformationFi Utility::copy(Containers::arrayCast(fieldData), destination); } else if(field._fieldType == SceneFieldType::Matrix3x3d) { Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 9), destination1f); + } else if(field._fieldType == SceneFieldType::Matrix3x2) { + expandTransformationMatrix(fieldData, destination); + } else if(field._fieldType == SceneFieldType::Matrix3x2d) { + expandTransformationMatrix(fieldData, destination); } else if(field._fieldType == SceneFieldType::DualComplex) { convertTransformation(fieldData, destination); } else if(field._fieldType == SceneFieldType::DualComplexd) { @@ -1525,6 +1545,10 @@ void SceneData::transformations3DIntoInternal(const UnsignedInt transformationFi Utility::copy(Containers::arrayCast(fieldData), destination); } else if(field._fieldType == SceneFieldType::Matrix4x4d) { Math::castInto(Containers::arrayCast<2, const Double>(fieldData, 16), destination1f); + } else if(field._fieldType == SceneFieldType::Matrix4x3) { + expandTransformationMatrix(fieldData, destination); + } else if(field._fieldType == SceneFieldType::Matrix4x3d) { + expandTransformationMatrix(fieldData, destination); } else if(field._fieldType == SceneFieldType::DualQuaternion) { convertTransformation(fieldData, destination); } else if(field._fieldType == SceneFieldType::DualQuaterniond) { diff --git a/src/Magnum/Trade/SceneData.h b/src/Magnum/Trade/SceneData.h index ea8d697e7..9bb087d17 100644 --- a/src/Magnum/Trade/SceneData.h +++ b/src/Magnum/Trade/SceneData.h @@ -127,9 +127,15 @@ enum class SceneField: UnsignedInt { * Transformation. Type is usually @ref SceneFieldType::Matrix3x3 for 2D * and @ref SceneFieldType::Matrix4x4 for 3D, but can be also any of * @relativeref{SceneFieldType,Matrix3x3d}, + * @relativeref{SceneFieldType,Matrix3x2} or + * @relativeref{SceneFieldType,Matrix3x2d} (with the bottom row implicitly + * assumed to be @f$ \begin{pmatrix} 0 & 0 & 1 \end{pmatrix} @f$), * @relativeref{SceneFieldType,DualComplex} or * @relativeref{SceneFieldType,DualComplexd} for 2D and * @relativeref{SceneFieldType,Matrix4x4d}, + * @relativeref{SceneFieldType,Matrix4x3} or + * @relativeref{SceneFieldType,Matrix4x3d} (with the bottom row implicitly + * assumed to be @f$ \begin{pmatrix} 0 & 0 & 0 & 1 \end{pmatrix} @f$), * @relativeref{SceneFieldType,DualQuaternion} or * @relativeref{SceneFieldType,DualQuaterniond} for 3D. An object should * have only one transformation, altough this isn't enforced in any way, @@ -2490,6 +2496,10 @@ namespace Implementation { type == SceneFieldType::Matrix3x3d || type == SceneFieldType::Matrix4x4 || type == SceneFieldType::Matrix4x4d || + type == SceneFieldType::Matrix3x2 || + type == SceneFieldType::Matrix3x2d || + type == SceneFieldType::Matrix4x3 || + type == SceneFieldType::Matrix4x3d || type == SceneFieldType::DualComplex || type == SceneFieldType::DualComplexd || type == SceneFieldType::DualQuaternion || diff --git a/src/Magnum/Trade/Test/SceneDataTest.cpp b/src/Magnum/Trade/Test/SceneDataTest.cpp index 1df17838a..b29000a8e 100644 --- a/src/Magnum/Trade/Test/SceneDataTest.cpp +++ b/src/Magnum/Trade/Test/SceneDataTest.cpp @@ -335,6 +335,8 @@ SceneDataTest::SceneDataTest() { addTests({&SceneDataTest::parentsIntoArrayInvalidSizeOrOffset, &SceneDataTest::transformations2DAsArray, &SceneDataTest::transformations2DAsArray, + &SceneDataTest::transformations2DAsArray, + &SceneDataTest::transformations2DAsArray, &SceneDataTest::transformations2DAsArray, &SceneDataTest::transformations2DAsArray, &SceneDataTest::transformations2DAsArrayTRS, @@ -349,6 +351,8 @@ SceneDataTest::SceneDataTest() { &SceneDataTest::transformations2DIntoArrayInvalidSizeOrOffsetTRS, &SceneDataTest::transformations3DAsArray, &SceneDataTest::transformations3DAsArray, + &SceneDataTest::transformations3DAsArray, + &SceneDataTest::transformations3DAsArray, &SceneDataTest::transformations3DAsArray, &SceneDataTest::transformations3DAsArray, &SceneDataTest::transformations3DAsArrayTRS, @@ -1779,8 +1783,12 @@ _c(Vector3) _c(Vector3d) _c(Matrix3) _c(Matrix3d) +_c(Matrix3x2) +_c(Matrix3x2d) _c(Matrix4) _c(Matrix4d) +_c(Matrix4x3) +_c(Matrix4x3d) _c(Complex) _c(Complexd) _c(Quaternion) @@ -1805,8 +1813,10 @@ template void SceneDataTest::constructMismatchedTRSDimensionality() { #endif SceneFieldData transformationMatrices2D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; + SceneFieldData transformationRectangularMatrices2D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; SceneFieldData transformations2D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; SceneFieldData transformationMatrices3D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; + SceneFieldData transformationRectangularMatrices3D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; SceneFieldData transformations3D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; SceneFieldData translations2D{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; SceneFieldData translations3D{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor>::type(), nullptr}; @@ -1821,6 +1831,10 @@ template void SceneDataTest::constructMismatchedTRSDimensionality() { SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationMatrices2D, translations3D}}; SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationMatrices2D, rotations3D}}; SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationMatrices2D, scalings3D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationRectangularMatrices2D, translations3D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationRectangularMatrices2D, rotations3D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationRectangularMatrices2D, scalings3D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformations2D, translations3D}}; SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformations2D, rotations3D}}; SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformations2D, scalings3D}}; @@ -1831,6 +1845,10 @@ template void SceneDataTest::constructMismatchedTRSDimensionality() { SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationMatrices3D, translations2D}}; SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationMatrices3D, rotations2D}}; SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationMatrices3D, scalings2D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationRectangularMatrices3D, translations2D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationRectangularMatrices3D, rotations2D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformationRectangularMatrices3D, scalings2D}}; + SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformations3D, translations2D}}; SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformations3D, rotations2D}}; SceneData{SceneObjectType::UnsignedInt, 0, nullptr, {transformations3D, scalings2D}}; @@ -1841,6 +1859,10 @@ template void SceneDataTest::constructMismatchedTRSDimensionality() { "Trade::SceneData: expected a 2D translation field but got Trade::SceneFieldType::{0}\n" "Trade::SceneData: expected a 2D rotation field but got Trade::SceneFieldType::{1}\n" "Trade::SceneData: expected a 2D scaling field but got Trade::SceneFieldType::{0}\n" + "Trade::SceneData: expected a 2D translation field but got Trade::SceneFieldType::{0}\n" + "Trade::SceneData: expected a 2D rotation field but got Trade::SceneFieldType::{1}\n" + "Trade::SceneData: expected a 2D scaling field but got Trade::SceneFieldType::{0}\n" + "Trade::SceneData: expected a 2D translation field but got Trade::SceneFieldType::{0}\n" "Trade::SceneData: expected a 2D rotation field but got Trade::SceneFieldType::{1}\n" "Trade::SceneData: expected a 2D scaling field but got Trade::SceneFieldType::{0}\n" @@ -1851,6 +1873,10 @@ template void SceneDataTest::constructMismatchedTRSDimensionality() { "Trade::SceneData: expected a 3D translation field but got Trade::SceneFieldType::{2}\n" "Trade::SceneData: expected a 3D rotation field but got Trade::SceneFieldType::{3}\n" "Trade::SceneData: expected a 3D scaling field but got Trade::SceneFieldType::{2}\n" + "Trade::SceneData: expected a 3D translation field but got Trade::SceneFieldType::{2}\n" + "Trade::SceneData: expected a 3D rotation field but got Trade::SceneFieldType::{3}\n" + "Trade::SceneData: expected a 3D scaling field but got Trade::SceneFieldType::{2}\n" + "Trade::SceneData: expected a 3D translation field but got Trade::SceneFieldType::{2}\n" "Trade::SceneData: expected a 3D rotation field but got Trade::SceneFieldType::{3}\n" "Trade::SceneData: expected a 3D scaling field but got Trade::SceneFieldType::{2}\n" @@ -2429,10 +2455,21 @@ void SceneDataTest::parentsIntoArrayInvalidSizeOrOffset() { "Trade::SceneData::parentsInto(): offset 4 out of bounds for a field of size 3\n"); } +template struct TransformationTypeFor { + typedef T Type; +}; +template struct TransformationTypeFor> { + typedef Math::Matrix3 Type; +}; +template struct TransformationTypeFor> { + typedef Math::Matrix4 Type; +}; + template void SceneDataTest::transformations2DAsArray() { setTestCaseTemplateName(NameTraits::name()); typedef typename T::Type U; + typedef typename TransformationTypeFor::Type TT; struct Transformation { UnsignedInt object; @@ -2451,12 +2488,12 @@ template void SceneDataTest::transformations2DAsArray() { {NoInit, 4, transformations}, {NoInit, 2, components} }; - transformations[0] = {1, T::translation({U(3.0), U(2.0)})}; - transformations[1] = {0, T::rotation(Math::Deg(35.0))}; - transformations[2] = {4, T::translation({U(1.5), U(2.5)})* - T::rotation(Math::Deg(-15.0))}; - transformations[3] = {5, T::rotation(Math::Deg(-15.0))* - T::translation({U(1.5), U(2.5)})}; + transformations[0] = {1, T{TT::translation({U(3.0), U(2.0)})}}; + transformations[1] = {0, T{TT::rotation(Math::Deg(35.0))}}; + transformations[2] = {4, T{TT::translation({U(1.5), U(2.5)})* + TT::rotation(Math::Deg(-15.0))}}; + transformations[3] = {5, T{TT::rotation(Math::Deg(-15.0))* + TT::translation({U(1.5), U(2.5)})}}; /* Object number 4 additionally has a scaling component (which isn't representable with dual complex numbers). It currently doesn't get added to the transformations returned from transformations2DInto() but that @@ -2980,6 +3017,7 @@ template void SceneDataTest::transformations3DAsArray() { setTestCaseTemplateName(NameTraits::name()); typedef typename T::Type U; + typedef typename TransformationTypeFor::Type TT; struct Transformation { UnsignedInt object; @@ -2998,15 +3036,15 @@ template void SceneDataTest::transformations3DAsArray() { {NoInit, 4, transformations}, {NoInit, 2, components} }; - transformations[0] = {1, T::translation({U(3.0), U(2.0), U(-0.5)})}; - transformations[1] = {0, T::rotation(Math::Deg(35.0), - Math::Vector3::yAxis())}; - transformations[2] = {4, T::translation({U(1.5), U(2.5), U(0.75)})* - T::rotation(Math::Deg(-15.0), - Math::Vector3::xAxis())}; - transformations[3] = {5, T::rotation(Math::Deg(-15.0), - Math::Vector3::xAxis())* - T::translation({U(1.5), U(2.5), U(0.75)})}; + transformations[0] = {1, T{TT::translation({U(3.0), U(2.0), U(-0.5)})}}; + transformations[1] = {0, T{TT::rotation(Math::Deg(35.0), + Math::Vector3::yAxis())}}; + transformations[2] = {4, T{TT::translation({U(1.5), U(2.5), U(0.75)})* + TT::rotation(Math::Deg(-15.0), + Math::Vector3::xAxis())}}; + transformations[3] = {5, T{TT::rotation(Math::Deg(-15.0), + Math::Vector3::xAxis())* + TT::translation({U(1.5), U(2.5), U(0.75)})}}; /* Object number 4 additionally has a scaling component (which isn't representable with dual quaternions). It currently doesn't get added to the transformations returned from transformations2DInto() but that