diff --git a/doc/changelog.dox b/doc/changelog.dox index cd386e7f7..1580e9105 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -73,6 +73,13 @@ See also: @ref Platform::AndroidApplication. This also makes the default framebuffer parameters consistent across the implementations. +@subsubsection changelog-latest-new-scenegraph SceneGraph library + +- New @ref SceneGraph::TranslationRotationScalingTransformation2D and + @ref SceneGraph::TranslationRotationScalingTransformation3D transformation + implementations that store separate translation, rotation and scaling + parameters for easier use with animated scenes + @subsubsection changelog-latest-new-trade Trade library - @ref Trade::ObjectData2D and @ref Trade::ObjectData3D now support also diff --git a/doc/scenegraph.dox b/doc/scenegraph.dox index 0f9752d2c..4e8c41c55 100644 --- a/doc/scenegraph.dox +++ b/doc/scenegraph.dox @@ -90,6 +90,12 @@ class for more detailed information: - @ref SceneGraph::BasicDualQuaternionTransformation "SceneGraph::DualQuaternionTransformation" --- 3D translation and rotation with fast inverse transformation and floating-point drift reduction +- @ref SceneGraph::BasicTranslationRotationScalingTransformation2D "SceneGraph::TranslationRotationScalingTransformation2D" --- + 2D transformations with separate translation, rotation and scaling. Usable + for scenes with animation tracks that control these properties separately. +- @ref SceneGraph::BasicTranslationRotationScalingTransformation3D "SceneGraph::TranslationRotationScalingTransformation3D" --- + 2D transformations with separate translation, rotation and scaling. Usable + for scenes with animation tracks that control these properties separately. - @ref SceneGraph::TranslationTransformation "SceneGraph::TranslationTransformation*D" --- Just 2D/3D translation (no rotation, scaling or anything else) diff --git a/src/Magnum/SceneGraph/CMakeLists.txt b/src/Magnum/SceneGraph/CMakeLists.txt index a56c0a984..95a12e144 100644 --- a/src/Magnum/SceneGraph/CMakeLists.txt +++ b/src/Magnum/SceneGraph/CMakeLists.txt @@ -62,6 +62,8 @@ set(MagnumSceneGraph_HEADERS Scene.h SceneGraph.h TranslationTransformation.h + TranslationRotationScalingTransformation2D.h + TranslationRotationScalingTransformation3D.h visibility.h) diff --git a/src/Magnum/SceneGraph/SceneGraph.h b/src/Magnum/SceneGraph/SceneGraph.h index cb6a0ba78..d3f641365 100644 --- a/src/Magnum/SceneGraph/SceneGraph.h +++ b/src/Magnum/SceneGraph/SceneGraph.h @@ -143,6 +143,11 @@ template using BasicTranslationTransformatio typedef BasicTranslationTransformation2D TranslationTransformation2D; typedef BasicTranslationTransformation3D TranslationTransformation3D; +template class BasicTranslationRotationScalingTransformation2D; +template class BasicTranslationRotationScalingTransformation3D; +typedef BasicTranslationRotationScalingTransformation2D TranslationRotationScalingTransformation2D; +typedef BasicTranslationRotationScalingTransformation3D TranslationRotationScalingTransformation3D; + namespace Implementation { template struct Transformation; } diff --git a/src/Magnum/SceneGraph/Test/CMakeLists.txt b/src/Magnum/SceneGraph/Test/CMakeLists.txt index b0dacc82d..21f369792 100644 --- a/src/Magnum/SceneGraph/Test/CMakeLists.txt +++ b/src/Magnum/SceneGraph/Test/CMakeLists.txt @@ -33,6 +33,8 @@ corrade_add_test(SceneGraphObjectTest ObjectTest.cpp LIBRARIES MagnumSceneGraphT corrade_add_test(SceneGraphRigidMatrixTrans___2DTest RigidMatrixTransformation2DTest.cpp LIBRARIES MagnumSceneGraphTestLib) corrade_add_test(SceneGraphRigidMatrixTrans___3DTest RigidMatrixTransformation3DTest.cpp LIBRARIES MagnumSceneGraphTestLib) corrade_add_test(SceneGraphSceneTest SceneTest.cpp LIBRARIES MagnumSceneGraph) +corrade_add_test(SceneGraphTranslationRotat___2DTest TranslationRotationScalingTransformation2DTest.cpp LIBRARIES MagnumSceneGraph) +corrade_add_test(SceneGraphTranslationRotat___3DTest TranslationRotationScalingTransformation3DTest.cpp LIBRARIES MagnumSceneGraph) corrade_add_test(SceneGraphTranslationTransfo___Test TranslationTransformationTest.cpp LIBRARIES MagnumSceneGraph) set_property(TARGET @@ -40,6 +42,8 @@ set_property(TARGET SceneGraphDualQuaternionTran___Test SceneGraphRigidMatrixTrans___2DTest SceneGraphRigidMatrixTrans___3DTest + SceneGraphTranslationRotat___2DTest + SceneGraphTranslationRotat___3DTest SceneGraphTranslationTransfo___Test APPEND PROPERTY COMPILE_DEFINITIONS "CORRADE_GRACEFUL_ASSERT") @@ -54,5 +58,7 @@ set_target_properties( SceneGraphRigidMatrixTrans___2DTest SceneGraphRigidMatrixTrans___3DTest SceneGraphSceneTest + SceneGraphTranslationRotat___2DTest + SceneGraphTranslationRotat___3DTest SceneGraphTranslationTransfo___Test PROPERTIES FOLDER "Magnum/SceneGraph/Test") diff --git a/src/Magnum/SceneGraph/Test/TranslationRotationScalingTransformation2DTest.cpp b/src/Magnum/SceneGraph/Test/TranslationRotationScalingTransformation2DTest.cpp new file mode 100644 index 000000000..4f54d9fe5 --- /dev/null +++ b/src/Magnum/SceneGraph/Test/TranslationRotationScalingTransformation2DTest.cpp @@ -0,0 +1,223 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 + 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 "Magnum/SceneGraph/TranslationRotationScalingTransformation2D.h" +#include "Magnum/SceneGraph/Scene.h" + +namespace Magnum { namespace SceneGraph { namespace Test { + +typedef Object Object2D; +typedef Scene Scene2D; + +struct TranslationRotationScalingTransformation2DTest: TestSuite::Tester { + explicit TranslationRotationScalingTransformation2DTest(); + + void fromMatrix(); + void toMatrix(); + void compose(); + void inverted(); + + void defaults(); + void setTransformation(); + void resetTransformation(); + + void translate(); + void rotate(); + void scale(); +}; + +TranslationRotationScalingTransformation2DTest::TranslationRotationScalingTransformation2DTest() { + addTests({&TranslationRotationScalingTransformation2DTest::fromMatrix, + &TranslationRotationScalingTransformation2DTest::toMatrix, + &TranslationRotationScalingTransformation2DTest::compose, + &TranslationRotationScalingTransformation2DTest::inverted, + + &TranslationRotationScalingTransformation2DTest::defaults, + &TranslationRotationScalingTransformation2DTest::setTransformation, + &TranslationRotationScalingTransformation2DTest::resetTransformation, + + &TranslationRotationScalingTransformation2DTest::translate, + &TranslationRotationScalingTransformation2DTest::rotate, + &TranslationRotationScalingTransformation2DTest::scale}); +} + +using namespace Math::Literals; + +void TranslationRotationScalingTransformation2DTest::fromMatrix() { + Matrix3 m = Matrix3::rotation(17.0_degf)*Matrix3::translation({1.0f, -0.3f}); + CORRADE_COMPARE(Implementation::Transformation::fromMatrix(m), m); +} + +void TranslationRotationScalingTransformation2DTest::toMatrix() { + Matrix3 m = Matrix3::rotation(17.0_degf)*Matrix3::translation({1.0f, -0.3f}); + CORRADE_COMPARE(Implementation::Transformation::toMatrix(m), m); +} + +void TranslationRotationScalingTransformation2DTest::compose() { + Matrix3 parent = Matrix3::rotation(17.0_degf); + Matrix3 child = Matrix3::translation({1.0f, -0.3f}); + CORRADE_COMPARE(Implementation::Transformation::compose(parent, child), parent*child); +} + +void TranslationRotationScalingTransformation2DTest::inverted() { + Matrix3 m = Matrix3::rotation(17.0_degf)*Matrix3::translation({1.0f, -0.3f}); + CORRADE_COMPARE(Implementation::Transformation::inverted(m)*m, Matrix3()); +} + +void TranslationRotationScalingTransformation2DTest::defaults() { + Object2D o; + CORRADE_COMPARE(o.translation(), Vector2{}); + CORRADE_COMPARE(o.rotation(), Complex{}); + CORRADE_COMPARE(o.scaling(), Vector2{1.0f}); + CORRADE_COMPARE(o.transformationMatrix(), Matrix3{}); +} + +void TranslationRotationScalingTransformation2DTest::setTransformation() { + /* Dirty after setting transformation */ + Object2D o; + o.setClean(); + CORRADE_VERIFY(!o.isDirty()); + o.setTransformation( + Matrix3::translation({7.0f, -1.0f})* + Matrix3::rotation(17.0_degf)* + Matrix3::scaling({1.5f, 0.5f})); + CORRADE_VERIFY(o.isDirty()); + CORRADE_COMPARE(o.translation(), (Vector2{7.0f, -1.0f})); + CORRADE_COMPARE(o.rotation(), Complex::rotation(17.0_degf)); + CORRADE_COMPARE(o.scaling(), (Vector2{1.5f, 0.5f})); + CORRADE_COMPARE(o.transformationMatrix(), + Matrix3::translation({7.0f, -1.0f})* + Matrix3::rotation(17.0_degf)* + Matrix3::scaling({1.5f, 0.5f})); + + /* Scene cannot be transformed */ + Scene2D s; + s.setClean(); + CORRADE_VERIFY(!s.isDirty()); + s.setTransformation(Matrix3::rotation(17.0_degf)); + CORRADE_VERIFY(!s.isDirty()); + CORRADE_COMPARE(s.transformationMatrix(), Matrix3()); +} + +void TranslationRotationScalingTransformation2DTest::resetTransformation() { + Object2D o; + o.rotate(17.0_degf); + CORRADE_VERIFY(o.transformationMatrix() != Matrix3()); + o.resetTransformation(); + CORRADE_COMPARE(o.translation(), Vector2{}); + CORRADE_COMPARE(o.rotation(), Complex{}); + CORRADE_COMPARE(o.scaling(), Vector2{1.0f}); + CORRADE_COMPARE(o.transformationMatrix(), Matrix3()); +} + +void TranslationRotationScalingTransformation2DTest::translate() { + { + Object2D o; + o.setTransformation(Matrix3::rotation(17.0_degf)); + o.translate({1.0f, -0.3f}) + .translate({1.0f, 0.1f}); + CORRADE_COMPARE(o.translation(), (Vector2{2.0f, -0.2f})); + CORRADE_COMPARE(o.rotation(), Complex::rotation(17.0_degf)); + CORRADE_COMPARE(o.scaling(), Vector2{1.0f}); + CORRADE_COMPARE(o.transformationMatrix(), + Matrix3::translation({1.0f, 0.1f})* + Matrix3::translation({1.0f, -0.3f})* + Matrix3::rotation(17.0_degf)); + } { + Object2D o; + o.setTransformation(Matrix3::rotation(17.0_degf)); + o.translateLocal({1.0f, -0.3f}) + .translateLocal({1.0f, 0.1f}); + CORRADE_COMPARE(o.translation(), (Vector2{2.0f, -0.2f})); + CORRADE_COMPARE(o.rotation(), Complex::rotation(17.0_degf)); + CORRADE_COMPARE(o.scaling(), Vector2{1.0f}); + CORRADE_COMPARE(o.transformationMatrix(), + Matrix3::translation({1.0f, -0.3f})* + Matrix3::translation({1.0f, 0.1f})* + Matrix3::rotation(17.0_degf)); + } +} + +void TranslationRotationScalingTransformation2DTest::rotate() { + { + Object2D o; + o.setTransformation(Matrix3::translation({1.0f, -0.3f})); + o.rotate(17.0_degf) + .rotate(-96.0_degf); + CORRADE_COMPARE(o.translation(), (Vector2{1.0f, -0.3f})); + CORRADE_COMPARE(o.rotation(), Complex::rotation(-79.0_degf)); + CORRADE_COMPARE(o.scaling(), Vector2{1.0f}); + CORRADE_COMPARE(o.transformationMatrix(), + Matrix3::translation({1.0f, -0.3f})* + Matrix3::rotation(-96.0_degf)* + Matrix3::rotation(17.0_degf)); + } { + Object2D o; + o.setTransformation(Matrix3::translation({1.0f, -0.3f})); + o.rotateLocal(17.0_degf) + .rotateLocal(-96.0_degf); + CORRADE_COMPARE(o.translation(), (Vector2{1.0f, -0.3f})); + CORRADE_COMPARE(o.rotation(), Complex::rotation(-79.0_degf)); + CORRADE_COMPARE(o.scaling(), Vector2{1.0f}); + CORRADE_COMPARE(o.transformationMatrix(), + Matrix3::translation({1.0f, -0.3f})* + Matrix3::rotation(17.0_degf)* + Matrix3::rotation(-96.0_degf)); + } +} + +void TranslationRotationScalingTransformation2DTest::scale() { + { + Object2D o; + o.setTransformation(Matrix3::rotation(17.0_degf)); + o.scale({1.0f, -0.3f}) + .scale({0.5f, 1.1f}); + CORRADE_COMPARE(o.translation(), Vector2{}); + CORRADE_COMPARE(o.rotation(), Complex::rotation(17.0_degf)); + CORRADE_COMPARE(o.scaling(), (Vector2{0.5f, -0.33f})); + CORRADE_COMPARE(o.transformationMatrix(), + Matrix3::rotation(17.0_degf)* + Matrix3::scaling({0.5f, 1.1f})* + Matrix3::scaling({1.0f, -0.3f})); + } { + Object2D o; + o.setTransformation(Matrix3::rotation(17.0_degf)); + o.scaleLocal({1.0f, -0.3f}) + .scaleLocal({0.5f, 1.1f}); + CORRADE_COMPARE(o.translation(), Vector2{}); + CORRADE_COMPARE(o.rotation(), Complex::rotation(17.0_degf)); + CORRADE_COMPARE(o.scaling(), (Vector2{0.5f, -0.33f})); + CORRADE_COMPARE(o.transformationMatrix(), + Matrix3::rotation(17.0_degf)* + Matrix3::scaling({1.0f, -0.3f})* + Matrix3::scaling({0.5f, 1.1f})); + } +} + +}}} + +CORRADE_TEST_MAIN(Magnum::SceneGraph::Test::TranslationRotationScalingTransformation2DTest) diff --git a/src/Magnum/SceneGraph/Test/TranslationRotationScalingTransformation3DTest.cpp b/src/Magnum/SceneGraph/Test/TranslationRotationScalingTransformation3DTest.cpp new file mode 100644 index 000000000..1b8fd4f24 --- /dev/null +++ b/src/Magnum/SceneGraph/Test/TranslationRotationScalingTransformation3DTest.cpp @@ -0,0 +1,239 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 + 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 "Magnum/SceneGraph/TranslationRotationScalingTransformation3D.h" +#include "Magnum/SceneGraph/Scene.h" + +namespace Magnum { namespace SceneGraph { namespace Test { + +typedef Object Object3D; +typedef Scene Scene3D; + +struct TranslationRotationScalingTransformation3DTest: TestSuite::Tester { + explicit TranslationRotationScalingTransformation3DTest(); + + void fromMatrix(); + void toMatrix(); + void compose(); + void inverted(); + + void defaults(); + void setTransformation(); + void resetTransformation(); + + void translate(); + void rotate(); + void scale(); +}; + +TranslationRotationScalingTransformation3DTest::TranslationRotationScalingTransformation3DTest() { + addTests({&TranslationRotationScalingTransformation3DTest::fromMatrix, + &TranslationRotationScalingTransformation3DTest::toMatrix, + &TranslationRotationScalingTransformation3DTest::compose, + &TranslationRotationScalingTransformation3DTest::inverted, + + &TranslationRotationScalingTransformation3DTest::defaults, + &TranslationRotationScalingTransformation3DTest::setTransformation, + &TranslationRotationScalingTransformation3DTest::resetTransformation, + + &TranslationRotationScalingTransformation3DTest::translate, + &TranslationRotationScalingTransformation3DTest::rotate, + &TranslationRotationScalingTransformation3DTest::scale}); +} + +using namespace Math::Literals; + +void TranslationRotationScalingTransformation3DTest::fromMatrix() { + Matrix4 m = Matrix4::rotationX(17.0_degf)*Matrix4::translation({1.0f, -0.3f, 2.3f})*Matrix4::scaling({2.0f, 1.4f, -2.1f}); + CORRADE_COMPARE(Implementation::Transformation::fromMatrix(m), m); +} + +void TranslationRotationScalingTransformation3DTest::toMatrix() { + Matrix4 m = Matrix4::rotationX(17.0_degf)*Matrix4::translation({1.0f, -0.3f, 2.3f})*Matrix4::scaling({2.0f, 1.4f, -2.1f}); + CORRADE_COMPARE(Implementation::Transformation::toMatrix(m), m); +} + +void TranslationRotationScalingTransformation3DTest::compose() { + Matrix4 parent = Matrix4::rotationX(17.0_degf); + Matrix4 child = Matrix4::translation({1.0f, -0.3f, 2.3f}); + CORRADE_COMPARE(Implementation::Transformation::compose(parent, child), parent*child); +} + +void TranslationRotationScalingTransformation3DTest::inverted() { + Matrix4 m = Matrix4::rotationX(17.0_degf)*Matrix4::translation({1.0f, -0.3f, 2.3f})*Matrix4::scaling({2.0f, 1.4f, -2.1f}); + CORRADE_COMPARE(Implementation::Transformation::inverted(m)*m, Matrix4()); +} + +void TranslationRotationScalingTransformation3DTest::defaults() { + Object3D o; + CORRADE_COMPARE(o.translation(), Vector3{}); + CORRADE_COMPARE(o.rotation(), Quaternion{}); + CORRADE_COMPARE(o.scaling(), Vector3{1.0f}); + CORRADE_COMPARE(o.transformationMatrix(), Matrix4{}); +} + +void TranslationRotationScalingTransformation3DTest::setTransformation() { + /* Dirty after setting transformation */ + Object3D o; + o.setClean(); + CORRADE_VERIFY(!o.isDirty()); + o.setTransformation( + Matrix4::translation({7.0f, -1.0f, 2.2f})* + Matrix4::rotationX(17.0_degf)* + Matrix4::scaling({1.5f, 0.5f, 3.0f})); + CORRADE_VERIFY(o.isDirty()); + CORRADE_COMPARE(o.translation(), (Vector3{7.0f, -1.0f, 2.2f})); + CORRADE_COMPARE(o.rotation(), Quaternion::rotation(17.0_degf, Vector3::xAxis())); + CORRADE_COMPARE(o.scaling(), (Vector3{1.5f, 0.5f, 3.0f})); + CORRADE_COMPARE(o.transformationMatrix(), + Matrix4::translation({7.0f, -1.0f, 2.2f})* + Matrix4::rotationX(17.0_degf)* + Matrix4::scaling({1.5f, 0.5f, 3.0f})); + + /* Scene cannot be transformed */ + Scene3D s; + s.setClean(); + CORRADE_VERIFY(!s.isDirty()); + s.setTransformation(Matrix4::rotationX(17.0_degf)); + CORRADE_VERIFY(!s.isDirty()); + CORRADE_COMPARE(s.transformationMatrix(), Matrix4()); +} + +void TranslationRotationScalingTransformation3DTest::resetTransformation() { + Object3D o; + o.rotateX(17.0_degf); + CORRADE_VERIFY(o.transformationMatrix() != Matrix4()); + o.resetTransformation(); + CORRADE_COMPARE(o.translation(), Vector3{}); + CORRADE_COMPARE(o.rotation(), Quaternion{}); + CORRADE_COMPARE(o.scaling(), Vector3{1.0f}); + CORRADE_COMPARE(o.transformationMatrix(), Matrix4()); +} + +void TranslationRotationScalingTransformation3DTest::translate() { + { + Object3D o; + o.setTransformation(Matrix4::rotationX(17.0_degf)); + o.translate({1.0f, -0.3f, 2.3f}) + .translate({1.0f, 0.1f, 0.2f}); + CORRADE_COMPARE(o.translation(), (Vector3{2.0f, -0.2f, 2.5f})); + CORRADE_COMPARE(o.rotation(), Quaternion::rotation(17.0_degf, Vector3::xAxis())); + CORRADE_COMPARE(o.scaling(), Vector3{1.0f}); + CORRADE_COMPARE(o.transformationMatrix(), + Matrix4::translation({1.0f, 0.1f, 0.2f})* + Matrix4::translation({1.0f, -0.3f, 2.3f})* + Matrix4::rotationX(17.0_degf)); + } { + Object3D o; + o.setTransformation(Matrix4::rotationX(17.0_degf)); + o.translateLocal({1.0f, -0.3f, 2.3f}) + .translateLocal({1.0f, 0.1f, 0.2f}); + CORRADE_COMPARE(o.translation(), (Vector3{2.0f, -0.2f, 2.5f})); + CORRADE_COMPARE(o.rotation(), Quaternion::rotation(17.0_degf, Vector3::xAxis())); + CORRADE_COMPARE(o.scaling(), Vector3{1.0f}); + CORRADE_COMPARE(o.transformationMatrix(), + Matrix4::translation({1.0f, -0.3f, 2.3f})* + Matrix4::translation({1.0f, 0.1f, 0.2f})* + Matrix4::rotationX(17.0_degf)); + } +} + +void TranslationRotationScalingTransformation3DTest::rotate() { + { + Object3D o; + o.setTransformation(Matrix4::translation({1.0f, -0.3f, 2.3f})); + o.rotateX(17.0_degf) + .rotateY(25.0_degf) + .rotateZ(-23.0_degf) + .rotate(96.0_degf, Vector3{1.0f/Constants::sqrt3()}); + CORRADE_COMPARE(o.translation(), (Vector3{1.0f, -0.3f, 2.3f})); + CORRADE_COMPARE(o.rotation(), + Quaternion::rotation(96.0_degf, Vector3{1.0f/Constants::sqrt3()})* + Quaternion::rotation(-23.0_degf, Vector3::zAxis())* + Quaternion::rotation(25.0_degf, Vector3::yAxis())* + Quaternion::rotation(17.0_degf, Vector3::xAxis())); + CORRADE_COMPARE(o.scaling(), Vector3{1.0f}); + CORRADE_COMPARE(o.transformationMatrix(), + Matrix4::translation({1.0f, -0.3f, 2.3f})* + Matrix4::rotation(96.0_degf, Vector3{1.0f/Constants::sqrt3()})* + Matrix4::rotationZ(-23.0_degf)* + Matrix4::rotationY(25.0_degf)* + Matrix4::rotationX(17.0_degf)); + } { + Object3D o; + o.setTransformation(Matrix4::translation({1.0f, -0.3f, 2.3f})); + o.rotateXLocal(17.0_degf) + .rotateYLocal(25.0_degf) + .rotateZLocal(-23.0_degf) + .rotateLocal(96.0_degf, Vector3{1.0f/Constants::sqrt3()}); + CORRADE_COMPARE(o.translation(), (Vector3{1.0f, -0.3f, 2.3f})); + CORRADE_COMPARE(o.rotation(), + Quaternion::rotation(17.0_degf, Vector3::xAxis())* + Quaternion::rotation(25.0_degf, Vector3::yAxis())* + Quaternion::rotation(-23.0_degf, Vector3::zAxis())* + Quaternion::rotation(96.0_degf, Vector3(1.0f/Constants::sqrt3()))); + CORRADE_COMPARE(o.scaling(), Vector3{1.0f}); + CORRADE_COMPARE(o.transformationMatrix(), + Matrix4::translation({1.0f, -0.3f, 2.3f})* + Matrix4::rotationX(17.0_degf)* + Matrix4::rotationY(25.0_degf)* + Matrix4::rotationZ(-23.0_degf)* + Matrix4::rotation(96.0_degf, Vector3{1.0f/Constants::sqrt3()})); + } +} + +void TranslationRotationScalingTransformation3DTest::scale() { + { + Object3D o; + o.setTransformation(Matrix4::rotationX(17.0_degf)); + o.scale({1.0f, -0.3f, 2.3f}) + .scale({0.5f, 1.1f, 2.0f}); + CORRADE_COMPARE(o.translation(), Vector3{}); + CORRADE_COMPARE(o.rotation(), Quaternion::rotation(17.0_degf, Vector3::xAxis())); + CORRADE_COMPARE(o.scaling(), (Vector3{0.5f, -0.33f, 4.6f})); + CORRADE_COMPARE(o.transformationMatrix(), + Matrix4::rotationX(17.0_degf)* + Matrix4::scaling({0.5f, 1.1f, 2.0f})* + Matrix4::scaling({1.0f, -0.3f, 2.3f})); + } { + Object3D o; + o.setTransformation(Matrix4::rotationX(17.0_degf)); + o.scaleLocal({1.0f, -0.3f, 2.3f}) + .scaleLocal({0.5f, 1.1f, 2.0f}); + CORRADE_COMPARE(o.translation(), Vector3{}); + CORRADE_COMPARE(o.rotation(), Quaternion::rotation(17.0_degf, Vector3::xAxis())); + CORRADE_COMPARE(o.scaling(), (Vector3{0.5f, -0.33f, 4.6f})); + CORRADE_COMPARE(o.transformationMatrix(), + Matrix4::rotationX(17.0_degf)* + Matrix4::scaling({1.0f, -0.3f, 2.3f})* + Matrix4::scaling({0.5f, 1.1f, 2.0f})); + } +} + +}}} + +CORRADE_TEST_MAIN(Magnum::SceneGraph::Test::TranslationRotationScalingTransformation3DTest) diff --git a/src/Magnum/SceneGraph/TranslationRotationScalingTransformation2D.h b/src/Magnum/SceneGraph/TranslationRotationScalingTransformation2D.h new file mode 100644 index 000000000..4b0cc3d7c --- /dev/null +++ b/src/Magnum/SceneGraph/TranslationRotationScalingTransformation2D.h @@ -0,0 +1,320 @@ +#ifndef Magnum_SceneGraph_TranslationRotationScalingTransformation2D_h +#define Magnum_SceneGraph_TranslationRotationScalingTransformation2D_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 + 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::SceneGraph::BasicTranslationRotationScalingTransformation2D, typedef @ref Magnum::SceneGraph::TranslationRotationScalingTransformation2D + */ + +#include "Magnum/Math/Complex.h" +#include "Magnum/Math/Matrix3.h" +#include "Magnum/SceneGraph/AbstractTranslationRotationScaling2D.h" +#include "Magnum/SceneGraph/Object.h" + +namespace Magnum { namespace SceneGraph { + +/** +@brief Two-dimensional transformation implemented using translation, rotation and scaling + +Similar to @ref BasicMatrixTransformation2D, but stores translation, rotation +and scaling separately. This makes it more suitable for e.g. animation, where +there are usually separate animation tracks for translation, rotation and +scaling. This separation also imposes some constraints --- for given object, +scaling is always applied first, rotation second and translation last. In +particular, unlike with matrix-based transformation implementation, it's not +possible to rotate a translated object, for example --- one has to apply the +rotation first and then translate using a rotated vector. +@see @ref scenegraph, @ref TranslationRotationScalingTransformation2D, + @ref BasicTranslationRotationScalingTransformation3D +*/ +template class BasicTranslationRotationScalingTransformation2D: public AbstractBasicTranslationRotationScaling2D { + public: + /** @brief Underlying transformation type */ + typedef Math::Matrix3 DataType; + + /** @brief Object transformation */ + Math::Matrix3 transformation() const; + + /** + * @brief Set transformation + * @return Reference to self (for method chaining) + * + * Expects that the transformation doesn't contain shear or reflection. + */ + Object>& setTransformation(const Math::Matrix3& transformation); + + /** @brief Object translation */ + Math::Vector2 translation() const { return _translation; } + + /** + * @brief Set translation + * @return Reference to self (for method chaining) + * + * Translation is always applied last, after rotation and scaling. + */ + Object>& setTranslation(const Math::Vector2& translation); + + /** @brief Object rotation */ + Math::Complex rotation() const { return _rotation; } + + /** + * @brief Set rotation + * @return Reference to self (for method chaining) + * + * Rotation is always applied after scaling and before translation. + * Expects that the quaternion is normalized. + */ + Object>& setRotation(const Math::Complex& rotation); + + /** @brief Object scaling */ + Math::Vector2 scaling() const { return _scaling; } + + /** + * @brief Set scaling + * @return Reference to self (for method chaining) + * + * Scaling is always applied first, before rotation and translation. + */ + Object>& setScaling(const Math::Vector2& scaling); + + /** @copydoc AbstractTranslationRotationScaling2D::resetTransformation() */ + Object>& resetTransformation() { + return setTransformation({}); + } + + /** + * @brief Translate object + * @return Reference to self (for method chaining) + * + * Note that translation is always applied last, after rotation and + * scaling. + * @see @ref translateLocal(), @ref Math::Vector2::xAxis(), + * @ref Math::Vector2::yAxis() + */ + Object>& translate(const Math::Vector2& vector) { + return setTranslation(vector + _translation); + } + + /** + * @brief Translate object as a local transformation + * + * Equivalent to the above, as translation is commutative. Note that + * translation is always applied last, after rotation and scaling. + */ + Object>& translateLocal(const Math::Vector2& vector) { + return setTranslation(_translation + vector); + } + + /** + * @brief Rotate object using a complex number + * @param complex Normalized complex number + * @return Reference to self (for method chaining) + * + * Note that rotation is always applied after scaling and before + * translation. Expects that the complex number is normalized. + * @see @ref rotate(Math::Rad), + * @ref rotateLocal(const Math::Complex&) + */ + Object>& rotate(const Math::Complex& complex) { + return setRotation(complex*_rotation); + } + + /** + * @brief Rotate object using a complex number as a local transformation + * + * Equivalent to the above, as 2D rotation is commutative. Note that + * rotation is always applied after scaling and before translation. + */ + Object>& rotateLocal(const Math::Complex& complex) { + return setRotation(_rotation*complex); + } + + /** + * @brief Rotate object + * @param angle Angle (counterclockwise) + * @return Reference to self (for method chaining) + * + * Same as calling @ref rotate(const Math::Complex&) with + * @ref Math::Complex::rotation(). Note that rotation is always applied after + * scaling and before translation. + * @see @ref rotateLocal() + */ + Object>& rotate(Math::Rad angle) { + return rotate(Math::Complex::rotation(angle)); + } + + /** + * @brief Rotate object as a local transformation + * + * Similar to the above, except that the rotation is applied before all + * other rotations. Note that rotation is always applied after scaling + * and before translation. Same as calling + * @ref rotateLocal(const Math::Complex&) with + * @ref Math::Complex::rotation(). + */ + Object>& rotateLocal(Math::Rad angle) { + return rotateLocal(Math::Complex::rotation(angle)); + } + + /** + * @brief Scale object + * @return Reference to self (for method chaining) + * + * Note that scaling is always applied first, before rotation and + * translation. + * @see @ref scaleLocal(), @ref Math::Vector2::xScale(), + * @ref Math::Vector2::yScale() + */ + Object>& scale(const Math::Vector2& vector) { + return setScaling(vector*_scaling); + } + + /** + * @brief Scale object as a local transformation + * + * Equivalent to the above, as scaling is commutative. Note that + * scaling is always first, before rotation and translation. + */ + Object>& scaleLocal(const Math::Vector2& vector) { + return setScaling(_scaling*vector); + } + + protected: + /* Allow construction only from Object */ + explicit BasicTranslationRotationScalingTransformation2D() = default; + + private: + void doResetTransformation() override final { resetTransformation(); } + + void doTranslate(const Math::Vector2& vector) override final { translate(vector); } + void doTranslateLocal(const Math::Vector2& vector) override final { translateLocal(vector); } + + void doRotate(Math::Rad angle) override final { + rotate(angle); + } + void doRotateLocal(Math::Rad angle) override final { + rotateLocal(angle); + } + + void doScale(const Math::Vector2& vector) override final { scale(vector); } + void doScaleLocal(const Math::Vector2& vector) override final { scaleLocal(vector); } + + Math::Vector2 _translation; + Math::Complex _rotation; + Math::Vector2 _scaling{T(1)}; +}; + +/** +@brief Two-dimensional transformation for float scenes implemented using translation, rotation and scaling + +@see @ref TranslationRotationScalingTransformation3D +*/ +typedef BasicTranslationRotationScalingTransformation2D TranslationRotationScalingTransformation2D; + +template Math::Matrix3 BasicTranslationRotationScalingTransformation2D::transformation() const { + return Math::Matrix3::from(_rotation.toMatrix(), _translation)* + Math::Matrix3::scaling(_scaling); +} + +template Object>& BasicTranslationRotationScalingTransformation2D::setTransformation(const Math::Matrix3& transformation) { + /* Setting transformation is forbidden for the scene */ + /** @todo Assert for this? */ + /** @todo Do this in some common code so we don't need to include Object? */ + if(!static_cast>*>(this)->isScene()) { + _translation = transformation.translation(); + _rotation = Math::Complex::fromMatrix(transformation.rotationShear()); + _scaling = transformation.scaling(); + static_cast>*>(this)->setDirty(); + } + + return static_cast>&>(*this); +} + +template Object>& BasicTranslationRotationScalingTransformation2D::setTranslation(const Math::Vector2& translation) { + /* Setting transformation is forbidden for the scene */ + /** @todo Assert for this? */ + /** @todo Do this in some common code so we don't need to include Object? */ + if(!static_cast>*>(this)->isScene()) { + _translation = translation; + static_cast>*>(this)->setDirty(); + } + + return static_cast>&>(*this); +} + +template Object>& BasicTranslationRotationScalingTransformation2D::setRotation(const Math::Complex& rotation) { + /* Setting transformation is forbidden for the scene */ + /** @todo Assert for this? */ + /** @todo Do this in some common code so we don't need to include Object? */ + if(!static_cast>*>(this)->isScene()) { + _rotation = rotation; + static_cast>*>(this)->setDirty(); + } + + return static_cast>&>(*this); +} + +template Object>& BasicTranslationRotationScalingTransformation2D::setScaling(const Math::Vector2& scaling) { + /* Setting transformation is forbidden for the scene */ + /** @todo Assert for this? */ + /** @todo Do this in some common code so we don't need to include Object? */ + if(!static_cast>*>(this)->isScene()) { + _scaling = scaling; + static_cast>*>(this)->setDirty(); + } + + return static_cast>&>(*this); +} + +namespace Implementation { + +template struct Transformation> { + constexpr static Math::Matrix3 fromMatrix(const Math::Matrix3& matrix) { + return matrix; + } + + constexpr static Math::Matrix3 toMatrix(const Math::Matrix3& transformation) { + return transformation; + } + + static Math::Matrix3 compose(const Math::Matrix3& parent, const Math::Matrix3& child) { + return parent*child; + } + + static Math::Matrix3 inverted(const Math::Matrix3& transformation) { + return transformation.inverted(); + } +}; + +} + +#if defined(CORRADE_TARGET_WINDOWS) && !defined(__MINGW32__) +extern template class MAGNUM_SCENEGRAPH_EXPORT Object>; +#endif + +}} + +#endif diff --git a/src/Magnum/SceneGraph/TranslationRotationScalingTransformation3D.h b/src/Magnum/SceneGraph/TranslationRotationScalingTransformation3D.h new file mode 100644 index 000000000..4ed96c2f3 --- /dev/null +++ b/src/Magnum/SceneGraph/TranslationRotationScalingTransformation3D.h @@ -0,0 +1,410 @@ +#ifndef Magnum_SceneGraph_TranslationRotationScalingTransformation3D_h +#define Magnum_SceneGraph_TranslationRotationScalingTransformation3D_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 + 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::SceneGraph::BasicTranslationRotationScalingTransformation3D, typedef @ref Magnum::SceneGraph::TranslationRotationScalingTransformation3D + */ + +#include "Magnum/Math/Matrix4.h" +#include "Magnum/Math/Quaternion.h" +#include "Magnum/SceneGraph/AbstractTranslationRotationScaling3D.h" +#include "Magnum/SceneGraph/Object.h" + +namespace Magnum { namespace SceneGraph { + +/** +@brief Three-dimensional transformation implemented using translation, rotation and scaling + +Similar to @ref BasicMatrixTransformation3D, but stores translation, rotation +and scaling separately. This makes it more suitable for e.g. animation, where +there are usually separate animation tracks for translation, rotation and +scaling. This separation also imposes some constraints --- for given object, +scaling is always applied first, rotation second and translation last. In +particular, unlike with matrix-based transformation implementation, it's not +possible to rotate a translated object, for example --- one has to apply the +rotation first and then translate using a rotated vector. +@see @ref scenegraph, @ref TranslationRotationScalingTransformation3D, + @ref BasicTranslationRotationScalingTransformation2D +*/ +template class BasicTranslationRotationScalingTransformation3D: public AbstractBasicTranslationRotationScaling3D { + public: + /** @brief Underlying transformation type */ + typedef Math::Matrix4 DataType; + + /** @brief Object transformation */ + Math::Matrix4 transformation() const; + + /** + * @brief Set transformation + * @return Reference to self (for method chaining) + * + * Expects that the transformation doesn't contain shear or reflection. + */ + Object>& setTransformation(const Math::Matrix4& transformation); + + /** @brief Object translation */ + Math::Vector3 translation() const { return _translation; } + + /** + * @brief Set translation + * @return Reference to self (for method chaining) + * + * Translation is always applied last, after rotation and scaling. + */ + Object>& setTranslation(const Math::Vector3& translation); + + /** @brief Object rotation */ + Math::Quaternion rotation() const { return _rotation; } + + /** + * @brief Set rotation + * @return Reference to self (for method chaining) + * + * Rotation is always applied after scaling and before translation. + * Expects that the quaternion is normalized. + */ + Object>& setRotation(const Math::Quaternion& rotation); + + /** @brief Object scaling */ + Math::Vector3 scaling() const { return _scaling; } + + /** + * @brief Set scaling + * @return Reference to self (for method chaining) + * + * Scaling is always applied first, before rotation and translation. + */ + Object>& setScaling(const Math::Vector3& scaling); + + /** @copydoc AbstractTranslationRotationScaling3D::resetTransformation() */ + Object>& resetTransformation() { + return setTransformation({}); + } + + /** + * @brief Translate object + * @return Reference to self (for method chaining) + * + * Note that translation is always applied last, after rotation and + * scaling. + * @see @ref translateLocal(), @ref Math::Vector3::xAxis(), + * @ref Math::Vector3::yAxis(), @ref Math::Vector3::zAxis() + */ + Object>& translate(const Math::Vector3& vector) { + return setTranslation(vector + _translation); + } + + /** + * @brief Translate object as a local transformation + * + * Equivalent to the above, as translation is commutative. Note that + * translation is always applied last, after rotation and scaling. + */ + Object>& translateLocal(const Math::Vector3& vector) { + return setTranslation(_translation + vector); + } + + /** + * @brief Rotate object using a quaternion + * @param quaternion Normalized quaternion + * @return Reference to self (for method chaining) + * + * Note that rotation is always applied after scaling and before + * translation. Expects that the quaternion is normalized. + * @see @ref rotate(Math::Rad, const Math::Vector3&), + * @ref rotateLocal(const Math::Quaternion&), @ref rotateX(), + * @ref rotateY(), @ref rotateZ(), @ref Math::Vector3::xAxis(), + * @ref Math::Vector3::yAxis(), @ref Math::Vector3::zAxis() + */ + Object>& rotate(const Math::Quaternion& quaternion) { + return setRotation(quaternion*_rotation); + } + + /** + * @brief Rotate object using a quaternion as a local transformation + * + * Similar to the above, except that the rotation is applied before all + * other rotations. Note that rotation is always applied after scaling + * and before translation. + */ + Object>& rotateLocal(const Math::Quaternion& quaternion) { + return setRotation(_rotation*quaternion); + } + + /** + * @brief Rotate object + * @param angle Angle (counterclockwise) + * @param normalizedAxis Normalized rotation axis + * @return Reference to self (for method chaining) + * + * Same as calling @ref rotate(const Math::Quaternion&) + * with @ref Quaternion::rotation(). Note that rotation is always + * applied after scaling and before translation. + * @see @ref rotateLocal(), @ref rotateX(), @ref rotateY(), + * @ref rotateZ(), @ref Math::Vector3::xAxis(), + * @ref Math::Vector3::yAxis(), @ref Math::Vector3::zAxis() + */ + Object>& rotate(Math::Rad angle, const Math::Vector3& normalizedAxis) { + return rotate(Math::Quaternion::rotation(angle, normalizedAxis)); + } + + /** + * @brief Rotate object as a local transformation + * + * Similar to the above, except that the rotation is applied before all + * other rotations. Note that rotation is always applied after scaling + * and before translation. Same as calling + * @ref rotateLocal(const Math::Quaternion&) with + * @ref Math::Quaternion::rotation(). + */ + Object>& rotateLocal(Math::Rad angle, const Math::Vector3& normalizedAxis) { + return rotateLocal(Math::Quaternion::rotation(angle, normalizedAxis)); + } + + /** + * @brief Rotate object around X axis + * @param angle Angle (counterclockwise) + * @return Reference to self (for method chaining) + * + * Same as calling @ref rotate() with @ref Math::Vector3::xAxis() as an + * axis. + * @see @ref rotateXLocal() + */ + Object>& rotateX(Math::Rad angle) { + return rotate(angle, Math::Vector3::xAxis()); + } + + /** + * @brief Rotate object around X axis as a local transformation + * + * Similar to the above, except that the rotation is applied before all + * other rotations. Note that rotation is always applied after scaling + * and before translation. Same as calling @ref rotateLocal() with + * @ref Math::Vector3::xAxis() as an axis. + */ + Object>& rotateXLocal(Math::Rad angle) { + return rotateLocal(angle, Math::Vector3::xAxis()); + } + + /** + * @brief Rotate object around Y axis + * @param angle Angle (counterclockwise) + * @return Reference to self (for method chaining) + * + * Same as calling @ref rotate() with @ref Math::Vector3::yAxis() as an + * axis. + * @see @ref rotateYLocal() + */ + Object>& rotateY(Math::Rad angle) { + return rotate(angle, Math::Vector3::yAxis()); + } + + /** + * @brief Rotate object around Y axis as a local transformation + * + * Similar to the above, except that the rotation is applied before all + * other rotations. Note that rotation is always applied after scaling + * and before translation. Same as calling @ref rotateLocal() with + * @ref Math::Vector3::yAxis() as an axis. + */ + Object>& rotateYLocal(Math::Rad angle) { + return rotateLocal(angle, Math::Vector3::yAxis()); + } + + /** + * @brief Rotate object around Z axis + * @param angle Angle (counterclockwise) + * @return Reference to self (for method chaining) + * + * Same as calling @ref rotate() with @ref Math::Vector3::yAxis() as an + * axis. + * @see @ref rotateZLocal() + */ + Object>& rotateZ(Math::Rad angle) { + return rotate(angle, Math::Vector3::zAxis()); + } + + /** + * @brief Rotate object around Z axis as a local transformation + * + * Similar to the above, except that the rotation is applied before all + * other rotations. Note that rotation is always applied after scaling + * and before translation. Same as calling @ref rotateLocal() with + * @ref Math::Vector3::zAxis() as an axis. + */ + Object>& rotateZLocal(Math::Rad angle) { + return rotateLocal(angle, Math::Vector3::zAxis()); + } + + /** + * @brief Scale object + * @return Reference to self (for method chaining) + * + * Note that scaling is always applied first, before rotation and + * translation. + * @see @ref scaleLocal(), @ref Math::Vector3::xScale(), + * @ref Math::Vector3::yScale(), @ref Math::Vector3::zScale() + */ + Object>& scale(const Math::Vector3& vector) { + return setScaling(vector*_scaling); + } + + /** + * @brief Scale object as a local transformation + * + * Equivalent to the above, as scaling is commutative. Note that + * scaling is always first, before rotation and translation. + */ + Object>& scaleLocal(const Math::Vector3& vector) { + return setScaling(_scaling*vector); + } + + protected: + /* Allow construction only from Object */ + explicit BasicTranslationRotationScalingTransformation3D() = default; + + private: + void doResetTransformation() override final { resetTransformation(); } + + void doTranslate(const Math::Vector3& vector) override final { translate(vector); } + void doTranslateLocal(const Math::Vector3& vector) override final { translateLocal(vector); } + + void doRotate(Math::Rad angle, const Math::Vector3& normalizedAxis) override final { + rotate(angle, normalizedAxis); + } + void doRotateLocal(Math::Rad angle, const Math::Vector3& normalizedAxis) override final { + rotateLocal(angle, normalizedAxis); + } + + void doRotateX(Math::Rad angle) override final { rotateX(angle); } + void doRotateXLocal(Math::Rad angle) override final { rotateXLocal(angle); } + + void doRotateY(Math::Rad angle) override final { rotateY(angle); } + void doRotateYLocal(Math::Rad angle) override final { rotateYLocal(angle); } + + void doRotateZ(Math::Rad angle) override final { rotateZ(angle); } + void doRotateZLocal(Math::Rad angle) override final { rotateZLocal(angle); } + + void doScale(const Math::Vector3& vector) override final { scale(vector); } + void doScaleLocal(const Math::Vector3& vector) override final { scaleLocal(vector); } + + Math::Vector3 _translation; + Math::Quaternion _rotation; + Math::Vector3 _scaling{T(1)}; +}; + +/** +@brief Three-dimensional transformation for float scenes implemented using translation, rotation and scaling + +@see @ref TranslationRotationScalingTransformation2D +*/ +typedef BasicTranslationRotationScalingTransformation3D TranslationRotationScalingTransformation3D; + +template Math::Matrix4 BasicTranslationRotationScalingTransformation3D::transformation() const { + return Math::Matrix4::from(_rotation.toMatrix(), _translation)* + Math::Matrix4::scaling(_scaling); +} + +template Object>& BasicTranslationRotationScalingTransformation3D::setTransformation(const Math::Matrix4& transformation) { + /* Setting transformation is forbidden for the scene */ + /** @todo Assert for this? */ + /** @todo Do this in some common code so we don't need to include Object? */ + if(!static_cast>*>(this)->isScene()) { + _translation = transformation.translation(); + _rotation = Math::Quaternion::fromMatrix(transformation.rotationShear()); + _scaling = transformation.scaling(); + static_cast>*>(this)->setDirty(); + } + + return static_cast>&>(*this); +} + +template Object>& BasicTranslationRotationScalingTransformation3D::setTranslation(const Math::Vector3& translation) { + /* Setting transformation is forbidden for the scene */ + /** @todo Assert for this? */ + /** @todo Do this in some common code so we don't need to include Object? */ + if(!static_cast>*>(this)->isScene()) { + _translation = translation; + static_cast>*>(this)->setDirty(); + } + + return static_cast>&>(*this); +} + +template Object>& BasicTranslationRotationScalingTransformation3D::setRotation(const Math::Quaternion& rotation) { + /* Setting transformation is forbidden for the scene */ + /** @todo Assert for this? */ + /** @todo Do this in some common code so we don't need to include Object? */ + if(!static_cast>*>(this)->isScene()) { + _rotation = rotation; + static_cast>*>(this)->setDirty(); + } + + return static_cast>&>(*this); +} + +template Object>& BasicTranslationRotationScalingTransformation3D::setScaling(const Math::Vector3& scaling) { + /* Setting transformation is forbidden for the scene */ + /** @todo Assert for this? */ + /** @todo Do this in some common code so we don't need to include Object? */ + if(!static_cast>*>(this)->isScene()) { + _scaling = scaling; + static_cast>*>(this)->setDirty(); + } + + return static_cast>&>(*this); +} + +namespace Implementation { + +template struct Transformation> { + constexpr static Math::Matrix4 fromMatrix(const Math::Matrix4& matrix) { + return matrix; + } + + constexpr static Math::Matrix4 toMatrix(const Math::Matrix4& transformation) { + return transformation; + } + + static Math::Matrix4 compose(const Math::Matrix4& parent, const Math::Matrix4& child) { + return parent*child; + } + + static Math::Matrix4 inverted(const Math::Matrix4& transformation) { + return transformation.inverted(); + } +}; + +} + +#if defined(CORRADE_TARGET_WINDOWS) && !defined(__MINGW32__) +extern template class MAGNUM_SCENEGRAPH_EXPORT Object>; +#endif + +}} + +#endif diff --git a/src/Magnum/SceneGraph/instantiation.cpp b/src/Magnum/SceneGraph/instantiation.cpp index 032728c32..fc5dd3d7f 100644 --- a/src/Magnum/SceneGraph/instantiation.cpp +++ b/src/Magnum/SceneGraph/instantiation.cpp @@ -36,6 +36,8 @@ #include "Magnum/SceneGraph/RigidMatrixTransformation2D.h" #include "Magnum/SceneGraph/RigidMatrixTransformation3D.h" #include "Magnum/SceneGraph/TranslationTransformation.h" +#include "Magnum/SceneGraph/TranslationRotationScalingTransformation2D.h" +#include "Magnum/SceneGraph/TranslationRotationScalingTransformation3D.h" namespace Magnum { namespace SceneGraph { @@ -75,6 +77,8 @@ template class MAGNUM_SCENEGRAPH_EXPORT_HPP Object>; template class MAGNUM_SCENEGRAPH_EXPORT_HPP Object>; template class MAGNUM_SCENEGRAPH_EXPORT_HPP Object>; +template class MAGNUM_SCENEGRAPH_EXPORT_HPP Object>; +template class MAGNUM_SCENEGRAPH_EXPORT_HPP Object>; template class MAGNUM_SCENEGRAPH_EXPORT_HPP Object>; template class MAGNUM_SCENEGRAPH_EXPORT_HPP Object>; #endif