diff --git a/src/SceneGraph/Object.h b/src/SceneGraph/Object.h index 34218ed4d..5dbfcdb45 100644 --- a/src/SceneGraph/Object.h +++ b/src/SceneGraph/Object.h @@ -205,9 +205,20 @@ template class MAGNUM_SCENEGRAPH_EXPORT Object: public Abs /** * @brief Set parent object * @return Pointer to self (for method chaining) + * + * @see setParentKeepTransformation() */ Object* setParent(Object* parent); + /** + * @brief Set parent object and keep absolute transformation + * @return Pointer to self (for method chaining) + * + * While setParent() preserves only relative transformation of the + * object, this funcition preserves absolute transformation. + */ + Object* setParentKeepTransformation(Object* parent); + /*@}*/ /** @{ @name Object transformation */ diff --git a/src/SceneGraph/Object.hpp b/src/SceneGraph/Object.hpp index 746258e7a..62bf892f1 100644 --- a/src/SceneGraph/Object.hpp +++ b/src/SceneGraph/Object.hpp @@ -87,6 +87,17 @@ template Object* Object::s return this; } +template Object* Object::setParentKeepTransformation(Object* parent) { + CORRADE_ASSERT(scene() == parent->scene(), "SceneGraph::Object::setParentKeepTransformation(): both parents must be in the same scene", this); + + const auto transformation = Transformation::compose( + Transformation::inverted(parent->absoluteTransformation()), absoluteTransformation()); + setParent(parent); + this->setTransformation(transformation); + + return this; +} + template typename Transformation::DataType Object::absoluteTransformation() const { if(!parent()) return Transformation::transformation(); return Transformation::compose(parent()->absoluteTransformation(), Transformation::transformation()); diff --git a/src/SceneGraph/Test/ObjectTest.cpp b/src/SceneGraph/Test/ObjectTest.cpp index a54acdd43..1e22753fb 100644 --- a/src/SceneGraph/Test/ObjectTest.cpp +++ b/src/SceneGraph/Test/ObjectTest.cpp @@ -36,6 +36,7 @@ class ObjectTest: public Corrade::TestSuite::Tester { void parenting(); void scene(); + void setParentKeepTransformation(); void absoluteTransformation(); void transformations(); void transformationsRelative(); @@ -66,6 +67,7 @@ class CachingObject: public Object3D, AbstractFeature<3> { ObjectTest::ObjectTest() { addTests({&ObjectTest::parenting, &ObjectTest::scene, + &ObjectTest::setParentKeepTransformation, &ObjectTest::absoluteTransformation, &ObjectTest::transformations, &ObjectTest::transformationsRelative, @@ -120,6 +122,31 @@ void ObjectTest::scene() { CORRADE_VERIFY(childOfOrphan->scene() == nullptr); } +void ObjectTest::setParentKeepTransformation() { + Object3D root; + root.rotateZ(Deg(35.0f)); + + Object3D* childOne = new Object3D(&root); + Object3D* childTwo = new Object3D(&root); + + childOne->translate(Vector3::xAxis(2.0f)); + childTwo->rotateY(Deg(90.0f)); + + /* Old parent and new parent must share the same scene */ + std::ostringstream o; + Error::setOutput(&o); + Scene3D scene; + childOne->setParentKeepTransformation(&scene); + CORRADE_COMPARE(o.str(), "SceneGraph::Object::setParentKeepTransformation(): both parents must be in the same scene\n"); + CORRADE_COMPARE(childOne->parent(), &root); + + /* Reparent to another and keep absolute transformation */ + auto transformation = childOne->absoluteTransformation(); + childOne->setParentKeepTransformation(childTwo); + CORRADE_VERIFY(childOne->parent() == childTwo); + CORRADE_COMPARE(childOne->absoluteTransformation(), transformation); +} + void ObjectTest::absoluteTransformation() { Scene3D s;