diff --git a/src/Object.cpp b/src/Object.cpp index a1e873d18..7973c6dff 100644 --- a/src/Object.cpp +++ b/src/Object.cpp @@ -41,6 +41,8 @@ void Object::setParent(Object* parent) { _parent->_children.erase(this); _parent = parent; + + setDirty(); } Matrix4 Object::transformation(bool absolute) { @@ -87,4 +89,25 @@ Scene* Object::scene() const { return 0; } +void Object::setDirty() { + /* The object (and all its children) are already dirty, nothing to do */ + if(dirty) return; + + dirty = true; + + /* Make all children dirty */ + for(set::iterator it = _children.begin(); it != _children.end(); ++it) + (*it)->setDirty(); +} + +void Object::setClean() { + /* The object (and all its parents) is already clean, nothing to do */ + if(!dirty) return; + + dirty = false; + + /* Make all parents clean */ + if(_parent != 0 && _parent != this) _parent->setClean(); +} + } diff --git a/src/Object.h b/src/Object.h index 142b5ffb0..10c825e67 100644 --- a/src/Object.h +++ b/src/Object.h @@ -45,7 +45,7 @@ class Object { * * Sets all transformations to their default values. */ - inline Object(Object* parent = 0): _parent(0) { + inline Object(Object* parent = 0): _parent(0), dirty(true) { setParent(parent); } @@ -85,7 +85,10 @@ class Object { virtual Matrix4 transformation(bool absolute = false); /** @brief Set transformation matrix */ - inline void setTransformation(const Matrix4& transformation) { _transformation = transformation; } + inline void setTransformation(const Matrix4& transformation) { + _transformation = transformation; + setDirty(); + } /** * @brief Multiply transformation matrix @@ -98,6 +101,7 @@ class Object { */ inline void multiplyTransformation(const Matrix4& transformation, bool global = true) { _transformation = global ? transformation*_transformation : _transformation*transformation; + setDirty(); } /** @@ -153,6 +157,54 @@ class Object { rotate(angle, Vector3(x, y, z), global); } + /** @{ @name Caching helpers + * + * If the object transformation is used many times when drawing (such + * as e.g. position of light object), it's good to cache these values, + * so they don't have to be computed again on every request. + * + * If the object or any parent is transformed, the transformed object + * and all its children are marked as dirty. If currently active camera + * is transformed, the scene goes through all children and calls + * setDirty() recursively on every clean object (if the object is + * already dirty, it and all its children are skipped, because they are + * dirty too). + * + * These functions are used to manage dirty status of the object. If + * the object doesn't cache anything, it's no need to bother about them, + * but if does, setClean() should be reimplemented and used to + * regenerate the cache. Thus the dirty status is managed only for + * these objects, which are calling setClean(). + */ + + /** + * @brief Whether the object is dirty + * @return True, if transformation of the object, any parent or camera + * has changed since last asking, false otherwise. + */ + inline bool isDirty() const { return dirty; } + + /** + * @brief Set object and all its children as dirty + * + * Recursively calls setDirty() on every child. If the object is already + * marked as dirty, the function does nothing. + * @attention Reimplementations must also call this function! + */ + virtual void setDirty(); + + /** + * @brief Set object and all its parents as clean + * + * Recursively calls setClean() on every parent. Default implementation + * only marks the object as clean, but if the object does any caching, + * this function should be reimplemented to regenerate the cache. + * @attention Reimplementations must also call this function! + */ + virtual void setClean(); + + /*@}*/ + /** * @brief Draw object * @@ -164,6 +216,7 @@ class Object { Object* _parent; std::set _children; Matrix4 _transformation; + bool dirty; }; } diff --git a/src/Test/ObjectTest.cpp b/src/Test/ObjectTest.cpp index 22a15959b..fb48ce867 100644 --- a/src/Test/ObjectTest.cpp +++ b/src/Test/ObjectTest.cpp @@ -62,4 +62,27 @@ void ObjectTest::scene() { QVERIFY(childOfOrphan->scene() == 0); } +void ObjectTest::dirty() { + Scene scene; + + Object* childOne = new Object(&scene); + Object* childTwo = new Object(childOne); + Object* childThree = new Object(childTwo); + + /* Object is dirty at the beginning */ + QVERIFY(childOne->isDirty()); + + /* Clean the object and all its parents */ + childThree->setClean(); + QVERIFY(!childThree->isDirty()); + QVERIFY(!childTwo->isDirty()); + QVERIFY(!childOne->isDirty()); + QVERIFY(!scene.isDirty()); + + /* Mark object and all its children as dirty */ + childTwo->setDirty(); + QVERIFY(childTwo->isDirty()); + QVERIFY(childThree->isDirty()); +} + }} diff --git a/src/Test/ObjectTest.h b/src/Test/ObjectTest.h index fccd4a903..13301d132 100644 --- a/src/Test/ObjectTest.h +++ b/src/Test/ObjectTest.h @@ -27,6 +27,7 @@ class ObjectTest: public QObject { private slots: void parenting(); void scene(); + void dirty(); }; }}