Browse Source

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.
pull/525/head
Vladimír Vondruš 5 years ago
parent
commit
e056ad3c99
  1. 24
      src/Magnum/Trade/SceneData.cpp
  2. 10
      src/Magnum/Trade/SceneData.h
  3. 68
      src/Magnum/Trade/Test/SceneDataTest.cpp

24
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<class Source, class Destination> 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<class Source, class Destination> void expandTransformationMatrix(const Containers::StridedArrayView1D<const void>& source, const Containers::StridedArrayView1D<Destination>& destination) {
CORRADE_INTERNAL_ASSERT(source.size() == destination.size());
const auto sourceT = Containers::arrayCast<const Source>(source);
for(std::size_t i = 0; i != sourceT.size(); ++i)
destination[i] = Destination{Math::RectangularMatrix<Source::Cols, Source::Rows, Float>{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<const Matrix3>(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<Matrix3x2>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::Matrix3x2d) {
expandTransformationMatrix<Matrix3x2d>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::DualComplex) {
convertTransformation<DualComplex>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::DualComplexd) {
@ -1525,6 +1545,10 @@ void SceneData::transformations3DIntoInternal(const UnsignedInt transformationFi
Utility::copy(Containers::arrayCast<const Matrix4>(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<Matrix4x3>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::Matrix4x3d) {
expandTransformationMatrix<Matrix4x3d>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::DualQuaternion) {
convertTransformation<DualQuaternion>(fieldData, destination);
} else if(field._fieldType == SceneFieldType::DualQuaterniond) {

10
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 ||

68
src/Magnum/Trade/Test/SceneDataTest.cpp

@ -335,6 +335,8 @@ SceneDataTest::SceneDataTest() {
addTests({&SceneDataTest::parentsIntoArrayInvalidSizeOrOffset,
&SceneDataTest::transformations2DAsArray<Matrix3>,
&SceneDataTest::transformations2DAsArray<Matrix3d>,
&SceneDataTest::transformations2DAsArray<Matrix3x2>,
&SceneDataTest::transformations2DAsArray<Matrix3x2d>,
&SceneDataTest::transformations2DAsArray<DualComplex>,
&SceneDataTest::transformations2DAsArray<DualComplexd>,
&SceneDataTest::transformations2DAsArrayTRS<Float, Float, Double>,
@ -349,6 +351,8 @@ SceneDataTest::SceneDataTest() {
&SceneDataTest::transformations2DIntoArrayInvalidSizeOrOffsetTRS,
&SceneDataTest::transformations3DAsArray<Matrix4>,
&SceneDataTest::transformations3DAsArray<Matrix4d>,
&SceneDataTest::transformations3DAsArray<Matrix4x3>,
&SceneDataTest::transformations3DAsArray<Matrix4x3d>,
&SceneDataTest::transformations3DAsArray<DualQuaternion>,
&SceneDataTest::transformations3DAsArray<DualQuaterniond>,
&SceneDataTest::transformations3DAsArrayTRS<Float, Double, Double>,
@ -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<class T> void SceneDataTest::constructMismatchedTRSDimensionality() {
#endif
SceneFieldData transformationMatrices2D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor<Math::Matrix3<T>>::type(), nullptr};
SceneFieldData transformationRectangularMatrices2D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor<Math::Matrix3x2<T>>::type(), nullptr};
SceneFieldData transformations2D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor<Math::DualComplex<T>>::type(), nullptr};
SceneFieldData transformationMatrices3D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor<Math::Matrix4<T>>::type(), nullptr};
SceneFieldData transformationRectangularMatrices3D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor<Math::Matrix4x3<T>>::type(), nullptr};
SceneFieldData transformations3D{SceneField::Transformation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor<Math::DualQuaternion<T>>::type(), nullptr};
SceneFieldData translations2D{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor<Math::Vector2<T>>::type(), nullptr};
SceneFieldData translations3D{SceneField::Translation, SceneObjectType::UnsignedInt, nullptr, Implementation::SceneFieldTypeFor<Math::Vector3<T>>::type(), nullptr};
@ -1821,6 +1831,10 @@ template<class T> 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<class T> 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<class T> 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<class T> 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<class T> struct TransformationTypeFor {
typedef T Type;
};
template<class T> struct TransformationTypeFor<Math::Matrix3x2<T>> {
typedef Math::Matrix3<T> Type;
};
template<class T> struct TransformationTypeFor<Math::Matrix4x3<T>> {
typedef Math::Matrix4<T> Type;
};
template<class T> void SceneDataTest::transformations2DAsArray() {
setTestCaseTemplateName(NameTraits<T>::name());
typedef typename T::Type U;
typedef typename TransformationTypeFor<T>::Type TT;
struct Transformation {
UnsignedInt object;
@ -2451,12 +2488,12 @@ template<class T> 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<U>(35.0))};
transformations[2] = {4, T::translation({U(1.5), U(2.5)})*
T::rotation(Math::Deg<U>(-15.0))};
transformations[3] = {5, T::rotation(Math::Deg<U>(-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<U>(35.0))}};
transformations[2] = {4, T{TT::translation({U(1.5), U(2.5)})*
TT::rotation(Math::Deg<U>(-15.0))}};
transformations[3] = {5, T{TT::rotation(Math::Deg<U>(-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<class T> void SceneDataTest::transformations3DAsArray() {
setTestCaseTemplateName(NameTraits<T>::name());
typedef typename T::Type U;
typedef typename TransformationTypeFor<T>::Type TT;
struct Transformation {
UnsignedInt object;
@ -2998,15 +3036,15 @@ template<class T> 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<U>(35.0),
Math::Vector3<U>::yAxis())};
transformations[2] = {4, T::translation({U(1.5), U(2.5), U(0.75)})*
T::rotation(Math::Deg<U>(-15.0),
Math::Vector3<U>::xAxis())};
transformations[3] = {5, T::rotation(Math::Deg<U>(-15.0),
Math::Vector3<U>::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<U>(35.0),
Math::Vector3<U>::yAxis())}};
transformations[2] = {4, T{TT::translation({U(1.5), U(2.5), U(0.75)})*
TT::rotation(Math::Deg<U>(-15.0),
Math::Vector3<U>::xAxis())}};
transformations[3] = {5, T{TT::rotation(Math::Deg<U>(-15.0),
Math::Vector3<U>::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

Loading…
Cancel
Save