diff --git a/src/Camera.cpp b/src/Camera.cpp index bbb791c73..db0e03ba0 100644 --- a/src/Camera.cpp +++ b/src/Camera.cpp @@ -14,13 +14,31 @@ */ #include "Camera.h" +#include "Scene.h" namespace Magnum { -Camera::Camera(Object* parent): Object(parent), viewportWidth(0), viewportHeight(0), _aspectRatioPolicy(Extend) { +Camera::Camera(Object* parent): Object(parent), _active(0), viewportWidth(0), viewportHeight(0), _aspectRatioPolicy(Extend) { setOrthographic(2, 1, 1000); } +void Camera::setActive(Scene* _scene) { + if(_scene == _active || scene() != _scene) return; + + Scene* oldActive = _active; + + /* Set camera active in new scene */ + _active = _scene; + if(_active) _active->setCamera(this); + + /* Remove the camera from current active scene, if the camera is still + active there */ + if(oldActive && oldActive->camera() == this) oldActive->setCamera(0); + + /* Clean the path to scene */ + setClean(); +} + void Camera::setOrthographic(GLfloat size, GLfloat near, GLfloat far) { /* Scale the volume down so it fits in (-1, 1) in all directions */ GLfloat xyScale = 2/size; @@ -55,11 +73,6 @@ void Camera::setPerspective(GLfloat fov, GLfloat near, GLfloat far) { fixAspectRatio(); } -Matrix4 Camera::cameraMatrix() const { - /** @todo How to do that? */ - return Matrix4(); -} - void Camera::setViewport(int width, int height) { glViewport(0, 0, width, height); @@ -68,6 +81,30 @@ void Camera::setViewport(int width, int height) { fixAspectRatio(); } +void Camera::setClean() { + if(!isDirty()) return; + _cameraMatrix = transformation(true).inverse(); + Object::setClean(); +} + +void Camera::setDirty() { + Object::setDirty(); + + /* Camera is active */ + if(_active) { + Scene* currentScene = scene(); + + /* Camera is not part of the scene anymore, remove it from there */ + if(!currentScene) _active->setCamera(0); + + /* Otherwise set the scene dirty */ + else _active->setDirty(); + + /* Clean up the path to scene immediately */ + setClean(); + } +} + void Camera::fixAspectRatio() { /* Don't divide by zero */ if(viewportWidth == 0 || viewportHeight == 0) { diff --git a/src/Camera.h b/src/Camera.h index 136bc3702..e4c760b4a 100644 --- a/src/Camera.h +++ b/src/Camera.h @@ -45,6 +45,20 @@ class Camera: public Object { */ Camera(Object* parent = 0); + /** + * @brief Scene in which the camera is active + * @return If the camera is not active anywhere, returns 0. + */ + inline Scene* active() const { return _active; } + + /** + * @brief Make camera active in given scene + * + * If passed 0 as @c scene and this camera is active in an scene, the + * camera will be removed from that scene. + */ + void setActive(Scene* scene); + /** @brief Aspect ratio policy */ AspectRatioPolicy aspectRatioPolicy() const { return _aspectRatioPolicy; } @@ -76,7 +90,10 @@ class Camera: public Object { * Camera matrix describes world position relative to the camera and is * applied as first. */ - Matrix4 cameraMatrix() const; + inline Matrix4 cameraMatrix() { + setClean(); + return _cameraMatrix; + } /** * @brief Projection matrix @@ -96,9 +113,23 @@ class Camera: public Object { */ void setViewport(int width, int height); + /** + * Recalculates camera matrix. + */ + virtual void setClean(); + + /** + * If the camera was active before and is still active, calls + * setDirty() on the scene, if is not part of the scene anymore, calls + * setCamera(0) on the scene. + */ + virtual void setDirty(); + private: Matrix4 rawProjectionMatrix; Matrix4 _projectionMatrix; + Matrix4 _cameraMatrix; + Scene* _active; int viewportWidth, viewportHeight; AspectRatioPolicy _aspectRatioPolicy; diff --git a/src/Scene.cpp b/src/Scene.cpp index d31826cb2..108ba3c4e 100644 --- a/src/Scene.cpp +++ b/src/Scene.cpp @@ -37,8 +37,22 @@ void Scene::setClearColor(const Magnum::Vector4& color) { } void Scene::setCamera(Camera* camera) { - camera->setViewport(viewportWidth, viewportHeight); + /* Don't assign the same camera or camera which is not part of the scene */ + if(camera == _camera || (camera && camera->scene() != this)) return; + + Camera* oldCamera = _camera; + + /* Set new camera active */ _camera = camera; + if(_camera) { + _camera->setViewport(viewportWidth, viewportHeight); + _camera->setActive(this); + } + + /* Set old camera inactive, if it is still active in this scene */ + if(oldCamera && oldCamera->active() == this) oldCamera->setActive(0); + + setDirty(); } void Scene::draw() { diff --git a/src/Scene.h b/src/Scene.h index ebb810d05..b146f2cfd 100644 --- a/src/Scene.h +++ b/src/Scene.h @@ -77,7 +77,11 @@ class Scene: public Object { if(_camera) _camera->setViewport(width, height); } - /** @brief Set camera */ + /** + * @brief Set camera + * + * If the camera is not part of the scene, the function does nothing. + */ void setCamera(Camera* camera); /** diff --git a/src/Test/CameraTest.cpp b/src/Test/CameraTest.cpp index 40bb77e4e..eace784c0 100644 --- a/src/Test/CameraTest.cpp +++ b/src/Test/CameraTest.cpp @@ -14,10 +14,12 @@ */ #include "CameraTest.h" -#include "Camera.h" #include +#include "Camera.h" +#include "Scene.h" + QTEST_APPLESS_MAIN(Magnum::Test::CameraTest) namespace Magnum { namespace Test { @@ -50,4 +52,24 @@ void CameraTest::perspective() { QVERIFY(camera.projectionMatrix() == Matrix4(a)); } +void CameraTest::active() { + Object* object = new Object; + Camera* camera = new Camera(object); + + Scene scene; + + /* Camera is not part of the scene, do nothing */ + scene.setCamera(camera); + QVERIFY(scene.camera() == 0); + + /* Add camera if the camera is part of the scene */ + object->setParent(&scene); + scene.setCamera(camera); + QVERIFY(scene.camera() == camera); + + /* When camera is taken out of the scene, remove it */ + object->setParent(0); + QVERIFY(scene.camera() == 0); +} + }} diff --git a/src/Test/CameraTest.h b/src/Test/CameraTest.h index 762e9c246..30e41fb91 100644 --- a/src/Test/CameraTest.h +++ b/src/Test/CameraTest.h @@ -25,6 +25,7 @@ class CameraTest: public QObject { private slots: void orthographic(); void perspective(); + void active(); }; }} diff --git a/src/Test/ObjectTest.cpp b/src/Test/ObjectTest.cpp index fb48ce867..3249aa422 100644 --- a/src/Test/ObjectTest.cpp +++ b/src/Test/ObjectTest.cpp @@ -83,6 +83,20 @@ void ObjectTest::dirty() { childTwo->setDirty(); QVERIFY(childTwo->isDirty()); QVERIFY(childThree->isDirty()); + + /* Set camera, makes everything dirty except path from camera to scene */ + Camera* camera = new Camera(&scene); + scene.setCamera(camera); + QVERIFY(childOne->isDirty()); + QVERIFY(!camera->isDirty()); + QVERIFY(!scene.isDirty()); + + /* Clean up and try to move the camera -> makes all dirty (except path + from camera to scene) */ + childThree->setClean(); + QVERIFY(!scene.isDirty()); + camera->translate(0, 0, 1); + QVERIFY(childOne->isDirty()); } }}