diff --git a/doc/changelog.dox b/doc/changelog.dox index 708a226fe..5e6f45333 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -843,6 +843,10 @@ See also: - @ref SceneGraph trees are now destructed in a way that preserves @ref SceneGraph::Object::parent() links up to the root as well as @ref SceneGraph::AbstractFeature::object() references +- Added @ref SceneGraph::Object::addFeature() as a counterpart to + @ref SceneGraph::AbstractObject::addFeature() that passes a concrete object + type to the feature constructor, in order to make it work with for example + the @ref BulletIntegration::MotionState @subsubsection changelog-latest-changes-scenetools SceneTools library diff --git a/src/Magnum/SceneGraph/Object.h b/src/Magnum/SceneGraph/Object.h index 1fa7e43c4..4a34d42b1 100644 --- a/src/Magnum/SceneGraph/Object.h +++ b/src/Magnum/SceneGraph/Object.h @@ -139,6 +139,18 @@ template class Object: public AbstractObject& operator=(Object&&) = delete; + /** + * @brief Add a feature + * @m_since_latest + * + * Like @ref AbstractObject::addFeature(), but passing a concrete + * object type to the feature constructor, instead of just the base + * @ref AbstractObject. + */ + template U& addFeature(Args&&... args) { + return *(new U{*this, std::forward(args)...}); + } + /** * @{ @name Scene hierarchy * diff --git a/src/Magnum/SceneGraph/Test/ObjectTest.cpp b/src/Magnum/SceneGraph/Test/ObjectTest.cpp index 05cf7ebbf..cce8678af 100644 --- a/src/Magnum/SceneGraph/Test/ObjectTest.cpp +++ b/src/Magnum/SceneGraph/Test/ObjectTest.cpp @@ -41,6 +41,7 @@ struct ObjectTest: TestSuite::Tester { explicit ObjectTest(); template void addFeature(); + template void addFeatureAbstractObject(); template void parenting(); template void addChild(); @@ -67,6 +68,8 @@ ObjectTest::ObjectTest() { addTests({ &ObjectTest::addFeature, &ObjectTest::addFeature, + &ObjectTest::addFeatureAbstractObject, + &ObjectTest::addFeatureAbstractObject, &ObjectTest::parenting, &ObjectTest::parenting, @@ -127,18 +130,38 @@ template void ObjectTest::addFeature() { class MyFeature: public AbstractBasicFeature3D { public: - explicit MyFeature(AbstractBasicObject3D& object, Int&, Containers::Pointer&&): AbstractBasicFeature3D{object} {} + explicit MyFeature(Object3D& object, Int&, Containers::Pointer&&): AbstractBasicFeature3D{object} {} }; Object3D o; CORRADE_VERIFY(o.features().isEmpty()); /* Test perfect forwarding as well */ int a = 0; + /* When called on Object3D, the feature constructor should get the concrete + Object type. AbstractObject::addFeature() is tested below. */ MyFeature& f = o.template addFeature(a, Containers::Pointer{}); CORRADE_VERIFY(!o.features().isEmpty()); CORRADE_COMPARE(&f.object(), &o); } +template void ObjectTest::addFeatureAbstractObject() { + setTestCaseTemplateName(Math::TypeTraits::name()); + + /* Like addFeature(), but calling the base variant on AbstractObject */ + + class MyFeature: public AbstractBasicFeature3D { + public: + explicit MyFeature(AbstractBasicObject3D& object, Int&, Containers::Pointer&&): AbstractBasicFeature3D{object} {} + }; + + Object3D o; + CORRADE_VERIFY(o.features().isEmpty()); + int a = 0; + MyFeature& f = static_cast&>(o).template addFeature(a, Containers::Pointer{}); + CORRADE_VERIFY(!o.features().isEmpty()); + CORRADE_COMPARE(&f.object(), &o); +} + template void ObjectTest::parenting() { setTestCaseTemplateName(Math::TypeTraits::name());