From bef497c46acb845bf2695e09957f54d12f28d03f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 7 Nov 2012 01:51:13 +0100 Subject: [PATCH] SceneGraph rework, part 7: Drawable and Camera features. * Camera is now templated also on underlying floating-point type. * Drawable objects can be split into groups (e.g. for separated rendering of transparent objects) * Added (long time missing) test for draw() function. --- src/SceneGraph/CMakeLists.txt | 14 +- src/SceneGraph/Camera.cpp | 115 +-------------- src/SceneGraph/Camera.h | 215 +++++++++++++++++++---------- src/SceneGraph/Camera.hpp | 145 +++++++++++++++++++ src/SceneGraph/Drawable.h | 192 ++++++++++++++++++++++++++ src/SceneGraph/Test/CMakeLists.txt | 1 + src/SceneGraph/Test/CameraTest.cpp | 89 +++++++++--- src/SceneGraph/Test/CameraTest.h | 1 + 8 files changed, 571 insertions(+), 201 deletions(-) create mode 100644 src/SceneGraph/Camera.hpp create mode 100644 src/SceneGraph/Drawable.h diff --git a/src/SceneGraph/CMakeLists.txt b/src/SceneGraph/CMakeLists.txt index 270a4f91a..2268b0dd5 100644 --- a/src/SceneGraph/CMakeLists.txt +++ b/src/SceneGraph/CMakeLists.txt @@ -1,4 +1,5 @@ -set(MagnumSceneGraph_SRCS ) +set(MagnumSceneGraph_SRCS + Camera.cpp) set(MagnumSceneGraph_HEADERS AbstractFeature.h AbstractGroupedFeature.h @@ -8,6 +9,9 @@ set(MagnumSceneGraph_HEADERS AbstractTranslationRotation3D.h AbstractTranslationRotationScaling2D.h AbstractTranslationRotationScaling3D.h + Camera.h + Camera.hpp + Drawable.h MatrixTransformation2D.h MatrixTransformation3D.h Object.h @@ -15,7 +19,7 @@ set(MagnumSceneGraph_HEADERS Scene.h magnumSceneGraphVisibility.h) -# add_library(MagnumSceneGraphObjects OBJECT ${MagnumSceneGraph_SRCS}) +add_library(MagnumSceneGraphObjects OBJECT ${MagnumSceneGraph_SRCS}) # Files compiled with different flags for main library and unit test library set(MagnumSceneGraph_GracefulAssert_SRCS @@ -24,11 +28,11 @@ set(MagnumSceneGraph_GracefulAssert_SRCS # Set shared library flags for the objects, as they will be part of shared lib # TODO: fix when CMake sets target_EXPORTS for OBJECT targets as well -# set_target_properties(MagnumSceneGraphObjects PROPERTIES COMPILE_FLAGS "-DMagnumSceneGraphObjects_EXPORTS ${CMAKE_SHARED_LIBRARY_CXX_FLAGS}") +set_target_properties(MagnumSceneGraphObjects PROPERTIES COMPILE_FLAGS "-DMagnumSceneGraphObjects_EXPORTS ${CMAKE_SHARED_LIBRARY_CXX_FLAGS}") # SceneGraph library add_library(MagnumSceneGraph SHARED -# $ + $ ${MagnumSceneGraph_GracefulAssert_SRCS}) target_link_libraries(MagnumSceneGraph Magnum) @@ -40,7 +44,7 @@ if(BUILD_TESTS) # Library with graceful assert for testing add_library(MagnumSceneGraphTestLib SHARED -# $ + $ ${MagnumSceneGraph_GracefulAssert_SRCS}) set_target_properties(MagnumSceneGraphTestLib PROPERTIES COMPILE_FLAGS -DCORRADE_GRACEFUL_ASSERT) target_link_libraries(MagnumSceneGraphTestLib Magnum) diff --git a/src/SceneGraph/Camera.cpp b/src/SceneGraph/Camera.cpp index d51a9ff7b..f76b3a6d1 100644 --- a/src/SceneGraph/Camera.cpp +++ b/src/SceneGraph/Camera.cpp @@ -14,121 +14,16 @@ */ #include "Camera.h" -#include "Scene.h" -using namespace std; +#include "Camera.hpp" namespace Magnum { namespace SceneGraph { #ifndef DOXYGEN_GENERATING_OUTPUT -namespace Implementation { - -template MatrixType aspectRatioFix(AspectRatioPolicy aspectRatioPolicy, const Vector2& projectionScale, const Math::Vector2& viewport) { - /* Don't divide by zero / don't preserve anything */ - if(projectionScale.x() == 0 || projectionScale.y() == 0 || viewport.x() == 0 || viewport.y() == 0 || aspectRatioPolicy == AspectRatioPolicy::NotPreserved) - return MatrixType(); - - Vector2 relativeAspectRatio = Vector2::from(viewport)*projectionScale; - - /* Extend on larger side = scale larger side down - Clip on smaller side = scale smaller side up */ - return Camera::aspectRatioScale( - (relativeAspectRatio.x() > relativeAspectRatio.y()) == (aspectRatioPolicy == AspectRatioPolicy::Extend) ? - Vector2(relativeAspectRatio.y()/relativeAspectRatio.x(), 1.0f) : - Vector2(1.0f, relativeAspectRatio.x()/relativeAspectRatio.y())); -} - -/* Explicitly instantiate the templates */ -template Matrix3 aspectRatioFix(AspectRatioPolicy, const Vector2&, const Math::Vector2&); -template Matrix4 aspectRatioFix(AspectRatioPolicy, const Vector2&, const Math::Vector2&); - -} +template class SCENEGRAPH_EXPORT AbstractCamera<2, GLfloat>; +template class SCENEGRAPH_EXPORT AbstractCamera<3, GLfloat>; +template class SCENEGRAPH_EXPORT Camera2D; +template class SCENEGRAPH_EXPORT Camera3D; #endif -template AbstractCamera::AbstractCamera(typename AbstractObject::ObjectType* parent): AbstractObject::ObjectType(parent), _aspectRatioPolicy(AspectRatioPolicy::NotPreserved) {} - -template typename AbstractObject::CameraType* AbstractCamera::setAspectRatioPolicy(AspectRatioPolicy policy) { - _aspectRatioPolicy = policy; - fixAspectRatio(); - return static_cast::CameraType*>(this); -} - -template void AbstractCamera::setViewport(const Math::Vector2& size) { - _viewport = size; - fixAspectRatio(); -} - -template void AbstractCamera::clean(const typename DimensionTraits::MatrixType& absoluteTransformation) { - AbstractObject::ObjectType::clean(absoluteTransformation); - - _cameraMatrix = absoluteTransformation.inverted(); -} - -template void AbstractCamera::draw() { - typename AbstractObject::SceneType* s = this->scene(); - CORRADE_ASSERT(s, "Camera: cannot draw without camera attached to scene", ); - - /* Recursively draw child objects */ - drawChildren(s, cameraMatrix()); -} - -template void AbstractCamera::drawChildren(typename AbstractObject::ObjectType* object, const typename DimensionTraits::MatrixType& transformationMatrix) { - for(typename AbstractObject::ObjectType* i = object->firstChild(); i; i = i->nextSibling()) { - /* Transformation matrix for the object */ - typename DimensionTraits::MatrixType matrix = transformationMatrix*i->transformation(); - - /* Draw the object and its children */ - i->draw(matrix, static_cast::CameraType*>(this)); - drawChildren(i, matrix); - } -} - -Camera2D* Camera2D::setProjection(const Vector2& size) { - /* Scale the volume down so it fits in (-1, 1) in all directions */ - rawProjectionMatrix = Matrix3::scaling(2.0f/size); - - fixAspectRatio(); - return this; -} - -Camera3D* Camera3D::setOrthographic(const Vector2& size, GLfloat near, GLfloat far) { - _near = near; - _far = far; - - Vector2 xyScale = 2.0f/size; - GLfloat zScale = 2.0f/(near-far); - - rawProjectionMatrix = Matrix4( - xyScale.x(), 0.0f, 0.0f, 0.0f, - 0.0f, xyScale.y(), 0.0f, 0.0f, - 0.0f, 0.0f, zScale, 0.0f, - 0.0f, 0.0f, near*zScale-1, 1.0f - ); - - fixAspectRatio(); - return this; -} - -Camera3D* Camera3D::setPerspective(GLfloat fov, GLfloat near, GLfloat far) { - _near = near; - _far = far; - - GLfloat xyScale = 1.0f/tan(fov/2); /* == near/size */ - GLfloat zScale = 1.0f/(near-far); - - rawProjectionMatrix = Matrix4( - xyScale, 0.0f, 0.0f, 0.0f, - 0.0f, xyScale, 0.0f, 0.0f, - 0.0f, 0.0f, (far+near)*zScale, -1.0f, - 0.0f, 0.0f, (2*far*near)*zScale, 0.0f - ); - - fixAspectRatio(); - return this; -} - -/* Explicitly instantiate the templates */ -template class AbstractCamera<2>; -template class AbstractCamera<3>; - }} diff --git a/src/SceneGraph/Camera.h b/src/SceneGraph/Camera.h index 21325afce..37d0bed94 100644 --- a/src/SceneGraph/Camera.h +++ b/src/SceneGraph/Camera.h @@ -16,10 +16,14 @@ */ /** @file - * @brief Class Magnum::SceneGraph::AbstractCamera, Magnum::SceneGraph::Camera2D, Magnum::SceneGraph::Camera3D + * @brief Class Magnum::SceneGraph::AbstractCamera, Magnum::SceneGraph::Camera2D, Magnum::SceneGraph::Camera3D, alias Magnum::SceneGraph::AbstractCamera2D, Magnum::SceneGraph::AbstractCamera3D */ -#include "Object.h" +#include "Math/Matrix3.h" +#include "Math/Matrix4.h" +#include "AbstractFeature.h" + +#include "magnumSceneGraphVisibility.h" #ifdef WIN32 /* I so HATE windows.h */ #undef near @@ -28,6 +32,14 @@ namespace Magnum { namespace SceneGraph { +template class Drawable; +template class FeatureGroup; +#ifndef MAGNUM_GCC46_COMPATIBILITY +template using DrawableGroup = FeatureGroup, T>; +#else +template class DrawableGroup; +#endif + /** @todo Export implementation symbols only for tests */ #ifndef DOXYGEN_GENERATING_OUTPUT @@ -36,18 +48,28 @@ namespace Implementation { NotPreserved, Extend, Clip }; - template MatrixType aspectRatioFix(AspectRatioPolicy aspectRatioPolicy, const Vector2& projectionScale, const Math::Vector2& viewport); - - /* These templates are instantiated in source file */ - extern template SCENEGRAPH_EXPORT Matrix3 aspectRatioFix(AspectRatioPolicy, const Vector2&, const Math::Vector2&); - extern template SCENEGRAPH_EXPORT Matrix4 aspectRatioFix(AspectRatioPolicy, const Vector2&, const Math::Vector2&); + template typename DimensionTraits::MatrixType aspectRatioFix(AspectRatioPolicy aspectRatioPolicy, const Math::Vector2& projectionScale, const Math::Vector2& viewport); } #endif /** -@brief %Camera object - */ -template class SCENEGRAPH_EXPORT AbstractCamera: public AbstractObject::ObjectType { +@brief Base for cameras + +See Drawable documentation for more information. + +@section AbstractCamera-explicit-specializations Explicit template specializations + +The following specialization are explicitly compiled into SceneGraph library. +For other specializations you have to use Camera.hpp implementation file to +avoid linker errors. See @ref compilation-speedup-hpp for more information. + + - @ref AbstractCamera "AbstractCamera<2>" + - @ref AbstractCamera "AbstractCamera<3>" + +@see Camera2D, Camera3D, Drawable, DrawableGroup, AbstractCamera2D, + AbstractCamera3D +*/ +template class SCENEGRAPH_EXPORT AbstractCamera: public AbstractFeature { public: /** * @brief Aspect ratio policy @@ -64,8 +86,13 @@ template class SCENEGRAPH_EXPORT AbstractCamera: public }; #endif - /** @copydoc AbstractObject::AbstractObject() */ - AbstractCamera(typename AbstractObject::ObjectType* parent = nullptr); + /** + * @brief Constructor + * @param object Object holding the camera + */ + inline AbstractCamera(AbstractObject* object): AbstractFeature(object), _aspectRatioPolicy(AspectRatioPolicy::NotPreserved) { + AbstractFeature::setCachedTransformations(AbstractFeature::CachedTransformation::InvertedAbsolute); + } virtual ~AbstractCamera() = 0; @@ -76,7 +103,7 @@ template class SCENEGRAPH_EXPORT AbstractCamera: public * @brief Set aspect ratio policy * @return Pointer to self (for method chaining) */ - typename AbstractObject::CameraType* setAspectRatioPolicy(AspectRatioPolicy policy); + AbstractCamera* setAspectRatioPolicy(AspectRatioPolicy policy); /** * @brief Camera matrix @@ -84,8 +111,8 @@ template class SCENEGRAPH_EXPORT AbstractCamera: public * Camera matrix describes world position relative to the camera and is * applied as first. */ - inline typename DimensionTraits::MatrixType cameraMatrix() { - this->setClean(); + inline typename DimensionTraits::MatrixType cameraMatrix() { + AbstractFeature::object()->setClean(); return _cameraMatrix; } @@ -96,7 +123,7 @@ template class SCENEGRAPH_EXPORT AbstractCamera: public * as last. * @see projectionSize() */ - inline typename DimensionTraits::MatrixType projectionMatrix() const { return _projectionMatrix; } + inline typename DimensionTraits::MatrixType projectionMatrix() const { return _projectionMatrix; } /** * @brief Size of (near) XY plane in current projection @@ -104,8 +131,8 @@ template class SCENEGRAPH_EXPORT AbstractCamera: public * Returns size of near XY plane computed from projection matrix. * @see projectionMatrix() */ - inline Vector2 projectionSize() const { - return {2.0f/_projectionMatrix[0].x(), 2.0f/_projectionMatrix[1].y()}; + inline Math::Vector2 projectionSize() const { + return {T(2.0)/_projectionMatrix[0].x(), T(2.0)/_projectionMatrix[1].y()}; } /** @brief Viewport size */ @@ -121,79 +148,95 @@ template class SCENEGRAPH_EXPORT AbstractCamera: public virtual void setViewport(const Math::Vector2& size); /** - * @brief Draw the scene + * @brief Draw * - * Draws the scene using drawChildren(). + * Draws given group of drawables. */ - virtual void draw(); - - using AbstractObject::ObjectType::draw; /* Don't hide Object's draw() */ + virtual void draw(DrawableGroup& group); protected: - /** - * Recalculates camera matrix. - */ - void clean(const typename DimensionTraits::MatrixType& absoluteTransformation); - - /** - * @brief Draw object children - * - * Recursively draws all children of the object. - */ - void drawChildren(typename AbstractObject::ObjectType* object, const typename DimensionTraits::MatrixType& transformationMatrix); + /** Recalculates camera matrix */ + inline void cleanInverted(const typename DimensionTraits::MatrixType& invertedAbsoluteTransformation) override { + _cameraMatrix = invertedAbsoluteTransformation; + } #ifndef DOXYGEN_GENERATING_OUTPUT inline void fixAspectRatio() { - _projectionMatrix = Implementation::aspectRatioFix::MatrixType>(_aspectRatioPolicy, {rawProjectionMatrix[0].x(), rawProjectionMatrix[1].y()}, _viewport)*rawProjectionMatrix; + _projectionMatrix = Implementation::aspectRatioFix(_aspectRatioPolicy, {rawProjectionMatrix[0].x(), rawProjectionMatrix[1].y()}, _viewport)*rawProjectionMatrix; } - typename DimensionTraits::MatrixType rawProjectionMatrix; + typename DimensionTraits::MatrixType rawProjectionMatrix; AspectRatioPolicy _aspectRatioPolicy; #endif private: - typename DimensionTraits::MatrixType _projectionMatrix; - typename DimensionTraits::MatrixType _cameraMatrix; + typename DimensionTraits::MatrixType _projectionMatrix; + typename DimensionTraits::MatrixType _cameraMatrix; Math::Vector2 _viewport; }; -template inline AbstractCamera::~AbstractCamera() {} +template inline AbstractCamera::~AbstractCamera() {} + +/** +@brief Base for two-dimensional cameras +Convenience alternative to %AbstractCamera<2, T>. See AbstractCamera +for more information. +@note Not available on GCC < 4.7. Use %AbstractCamera<2, T> instead. +@see AbstractCamera3D +@todoc Remove workaround when Doxygen supports alias template +*/ #ifndef DOXYGEN_GENERATING_OUTPUT -namespace Implementation { - template class Camera {}; +#ifndef MAGNUM_GCC46_COMPATIBILITY +template using AbstractCamera2D = AbstractCamera<2, T>; +#endif +#else +typedef AbstractCamera<2, T = GLfloat> AbstractCamera2D; +#endif - template<> class Camera<2> { - public: - inline constexpr static Matrix3 aspectRatioScale(const Vector2& scale) { - return Matrix3::scaling({scale.x(), scale.y()}); - } - }; - template<> class Camera<3> { - public: - inline constexpr static Matrix4 aspectRatioScale(const Vector2& scale) { - return Matrix4::scaling({scale.x(), scale.y(), 1.0f}); - } - }; -} +/** +@brief Base for three-dimensional cameras + +Convenience alternative to %AbstractCamera<3, T>. See AbstractCamera +for more information. +@note Not available on GCC < 4.7. Use %AbstractCamera<3, T> instead. +@see AbstractCamera2D +@todoc Remove workaround when Doxygen supports alias template +*/ +#ifndef DOXYGEN_GENERATING_OUTPUT +#ifndef MAGNUM_GCC46_COMPATIBILITY +template using AbstractCamera3D = AbstractCamera<3, T>; +#endif +#else +typedef AbstractCamera<3, T = GLfloat> AbstractCamera3D; #endif /** -@brief %Camera for two-dimensional scenes +@brief Camera for two-dimensional scenes + +See Drawable documentation for more information. + +@section Object-explicit-specializations Explicit template specializations -@see Camera3D +The following specialization are explicitly compiled into SceneGraph library. +For other specializations you have to use Camera.hpp implementation file to +avoid linker errors. See @ref compilation-speedup-hpp for more information. + + - @ref Camera2D "Camera2D" + +@see Camera3D, Drawable, DrawableGroup */ -class SCENEGRAPH_EXPORT Camera2D: public AbstractCamera<2> { +template class SCENEGRAPH_EXPORT Camera2D: public AbstractCamera<2, T> { public: /** * @brief Constructor - * @param parent Parent object + * @param object %Object holding this feature * * Sets orthographic projection to the default OpenGL cube (range @f$ [-1; 1] @f$ in all directions). * @see setOrthographic() */ - inline Camera2D(Object2D* parent = nullptr): AbstractCamera<2>(parent) {} + inline Camera2D(AbstractObject<2, T>* object): AbstractCamera<2, T>(object) {} /** * @brief Set projection @@ -203,24 +246,42 @@ class SCENEGRAPH_EXPORT Camera2D: public AbstractCamera<2> { * The area of given size will be scaled down to range @f$ [-1; 1] @f$ * on all directions. */ - Camera2D* setProjection(const Vector2& size); + Camera2D* setProjection(const Math::Vector2& size); + + /* Overloads to remove WTF-factor from method chaining order */ + #ifndef DOXYGEN_GENERATING_OUTPUT + inline Camera2D* setAspectRatioPolicy(typename AbstractCamera<2, T>::AspectRatioPolicy policy) { + AbstractCamera<2, T>::setAspectRatioPolicy(policy); + return this; + } + #endif }; /** -@brief %Camera for three-dimensional scenes +@brief Camera for three-dimensional scenes + +See Drawable documentation for more information. + +@section Object-explicit-specializations Explicit template specializations + +The following specialization are explicitly compiled into SceneGraph library. +For other specializations you have to use Camera.hpp implementation file to +avoid linker errors. See @ref compilation-speedup-hpp for more information. + + - @ref Camera3D "Camera3D" -@see Camera2D +@see Camera2D, Drawable, DrawableGroup */ -class SCENEGRAPH_EXPORT Camera3D: public AbstractCamera<3> { +template class SCENEGRAPH_EXPORT Camera3D: public AbstractCamera<3, T> { public: /** * @brief Constructor - * @param parent Parent object + * @param object %Object holding this feature * * Sets orthographic projection to the default OpenGL cube (range @f$ [-1; 1] @f$ in all directions). * @see setOrthographic(), setPerspective() */ - inline Camera3D(Object3D* parent = nullptr): AbstractCamera<3>(parent), _near(0.0f), _far(0.0f) {} + inline Camera3D(AbstractObject<3, T>* object): AbstractCamera<3, T>(object), _near(0.0f), _far(0.0f) {} /** * @brief Set orthographic projection @@ -232,7 +293,7 @@ class SCENEGRAPH_EXPORT Camera3D: public AbstractCamera<3> { * The volume of given size will be scaled down to range @f$ [-1; 1] @f$ * on all directions. */ - Camera3D* setOrthographic(const Vector2& size, GLfloat near, GLfloat far); + Camera3D* setOrthographic(const Math::Vector2& size, T near, T far); /** * @brief Set perspective projection @@ -243,18 +304,32 @@ class SCENEGRAPH_EXPORT Camera3D: public AbstractCamera<3> { * * @todo Aspect ratio */ - Camera3D* setPerspective(GLfloat fov, GLfloat near, GLfloat far); + Camera3D* setPerspective(T fov, T near, T far); /** @brief Near clipping plane */ - inline GLfloat near() const { return _near; } + inline T near() const { return _near; } /** @brief Far clipping plane */ - inline GLfloat far() const { return _far; } + inline T far() const { return _far; } + + /* Overloads to remove WTF-factor from method chaining order */ + #ifndef DOXYGEN_GENERATING_OUTPUT + inline Camera3D* setAspectRatioPolicy(typename AbstractCamera<3, T>::AspectRatioPolicy policy) { + AbstractCamera<3, T>::setAspectRatioPolicy(policy); + return this; + } + #endif private: - GLfloat _near, _far; + T _near, _far; }; +/* Make implementers' life easier */ +#ifndef MAGNUM_GCC46_COMPATIBILITY +template using DrawableGroup2D = DrawableGroup<2, T>; +template using DrawableGroup3D = DrawableGroup<3, T>; +#endif + }} #endif diff --git a/src/SceneGraph/Camera.hpp b/src/SceneGraph/Camera.hpp new file mode 100644 index 000000000..2d1e7a42a --- /dev/null +++ b/src/SceneGraph/Camera.hpp @@ -0,0 +1,145 @@ +#ifndef Magnum_SceneGraph_Camera_hpp +#define Magnum_SceneGraph_Camera_hpp +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief @ref compilation-speedup-hpp "Template implementation" for Camera.h + */ + +#include "Camera.h" + +#include + +#include "Drawable.h" +#include "Scene.h" + +using namespace std; + +namespace Magnum { namespace SceneGraph { + +#ifndef DOXYGEN_GENERATING_OUTPUT +namespace Implementation { + +template class Camera {}; + +template class Camera<2, T> { + public: + inline constexpr static Math::Matrix3 aspectRatioScale(const Math::Vector2& scale) { + return Math::Matrix3::scaling({scale.x(), scale.y()}); + } +}; +template class Camera<3, T> { + public: + inline constexpr static Math::Matrix4 aspectRatioScale(const Math::Vector2& scale) { + return Math::Matrix4::scaling({scale.x(), scale.y(), 1.0f}); + } +}; + +template typename DimensionTraits::MatrixType aspectRatioFix(AspectRatioPolicy aspectRatioPolicy, const Math::Vector2& projectionScale, const Math::Vector2& viewport) { + /* Don't divide by zero / don't preserve anything */ + if(projectionScale.x() == 0 || projectionScale.y() == 0 || viewport.x() == 0 || viewport.y() == 0 || aspectRatioPolicy == AspectRatioPolicy::NotPreserved) + return {}; + + Math::Vector2 relativeAspectRatio = Math::Vector2::from(viewport)*projectionScale; + + /* Extend on larger side = scale larger side down + Clip on smaller side = scale smaller side up */ + return Camera::aspectRatioScale( + (relativeAspectRatio.x() > relativeAspectRatio.y()) == (aspectRatioPolicy == AspectRatioPolicy::Extend) ? + Vector2(relativeAspectRatio.y()/relativeAspectRatio.x(), T(1.0)) : + Vector2(T(1.0), relativeAspectRatio.x()/relativeAspectRatio.y())); +} + +} +#endif + +template AbstractCamera* AbstractCamera::setAspectRatioPolicy(AspectRatioPolicy policy) { + _aspectRatioPolicy = policy; + fixAspectRatio(); + return this; +} + +template void AbstractCamera::setViewport(const Math::Vector2& size) { + _viewport = size; + fixAspectRatio(); +} + +template void AbstractCamera::draw(DrawableGroup& group) { + AbstractObject* scene = AbstractFeature::object()->sceneObject(); + CORRADE_ASSERT(scene, "Camera::draw(): cannot draw when camera is not part of any scene", ); + + /* Compute camera matrix */ + AbstractFeature::object()->setClean(); + + /* Compute transformations of all objects in the group relative to the camera */ + std::vector*> objects(group.size()); + for(std::size_t i = 0; i != group.size(); ++i) + objects[i] = group[i]->object(); + std::vector::MatrixType> transformations = + scene->transformationMatrices(objects, _cameraMatrix); + + /* Perform the drawing */ + for(std::size_t i = 0; i != transformations.size(); ++i) + group[i]->draw(transformations[i], this); +} + +template Camera2D* Camera2D::setProjection(const Math::Vector2& size) { + /* Scale the volume down so it fits in (-1, 1) in all directions */ + AbstractCamera<2, T>::rawProjectionMatrix = Math::Matrix3::scaling(2.0f/size); + + AbstractCamera<2, T>::fixAspectRatio(); + return this; +} + +template Camera3D* Camera3D::setOrthographic(const Math::Vector2& size, T near, T far) { + _near = near; + _far = far; + + Math::Vector2 xyScale = T(2.0)/size; + T zScale = T(2.0)/(near-far); + + AbstractCamera<3, T>::rawProjectionMatrix = Math::Matrix4( + xyScale.x(), T(0.0), T(0.0), T(0.0), + T(0.0), xyScale.y(), T(0.0), T(0.0), + T(0.0), T(0.0), zScale, T(0.0), + T(0.0), T(0.0), near*zScale-1, T(1.0) + ); + + AbstractCamera<3, T>::fixAspectRatio(); + return this; +} + +template Camera3D* Camera3D::setPerspective(T fov, T near, T far) { + _near = near; + _far = far; + + T xyScale = T(1.0)/tan(fov/2); /* == near/size */ + T zScale = T(1.0)/(near-far); + + AbstractCamera<3, T>::rawProjectionMatrix = Matrix4( + xyScale, T(0.0), T(0.0), T(0.0), + T(0.0), xyScale, T(0.0), T(0.0), + T(0.0), T(0.0), (far+near)*zScale, T(-1.0), + T(0.0), T(0.0), (2*far*near)*zScale, T(0.0) + ); + + AbstractCamera<3, T>::fixAspectRatio(); + return this; +} + +}} + +#endif diff --git a/src/SceneGraph/Drawable.h b/src/SceneGraph/Drawable.h new file mode 100644 index 000000000..7b93cc061 --- /dev/null +++ b/src/SceneGraph/Drawable.h @@ -0,0 +1,192 @@ +#ifndef Magnum_SceneGraph_Drawable_h +#define Magnum_SceneGraph_Drawable_h +/* + Copyright © 2010, 2011, 2012 Vladimír Vondruš + + This file is part of Magnum. + + Magnum is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 3 + only, as published by the Free Software Foundation. + + Magnum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License version 3 for more details. +*/ + +/** @file + * @brief Class Magnum::SceneGraph::Drawable, Magnum::SceneGraph::DrawableGroup, alias Magnum::SceneGraph::Drawable2D, Magnum::SceneGraph::Drawable3D, Magnum::SceneGraph::DrawableGroup2D, Magnum::SceneGraph::DrawableGroup3D + */ + +#include "AbstractGroupedFeature.h" + +namespace Magnum { namespace SceneGraph { + +template class AbstractCamera; +template class Drawable; +#ifndef MAGNUM_GCC46_COMPATIBILITY +template using DrawableGroup = FeatureGroup, T>; +#else +template class DrawableGroup; +#endif + +/** +@brief %Drawable + +Adds drawing function to object. Each %Drawable is part of some DrawableGroup +and the whole group is drawn with particular camera using AbstractCamera::draw(). + +@section Drawable-usage Usage + +First thing is add Drawable feature to some object and implement draw(). You +can do it conveniently using multiple inheritance (see @ref scenegraph-features +for introduction). Example: +@code +typedef SceneGraph::Object> Object3D; +typedef SceneGraph::Scene> Scene3D; + +class DrawableObject: public Object3D, SceneGraph::Drawable3D<> { + public: + DrawableObject(Object* parent, SceneGraph::DrawableGroup3D<>* group): Object3D(parent), SceneGraph::Drawable3D<>(this, group) { + // ... + } + + void draw(const Matrix4& transformationMatrix, AbstractCamera3D<>* camera) override { + // ... + } +} +@endcode + +Then you add these objects to your scene and some drawable group and transform +them as you like: +@code +Scene3D scene; +SceneGraph::DrawableGroup3D<> group; + +(new DrawableObject(&scene, &group)) + ->translate(Vector3::yAxis(-0.3f)) + ->rotateX(deg(30.0f)); +(new AnotherDrawableObject(&scene, &group)) + ->translate(Vector3::zAxis(0.5f)); +// ... +@endcode + +The last thing you need is Camera attached to some object (thus using its +transformation) and with it you can perform drawing in your draw event: +@code +Camera3D<> camera(&cameraObject); + +void MyApplication::drawEvent() { + camera.draw(&group); +} +@endcode + +@see Drawable2D, Drawable3D, DrawableGroup2D, DrawableGroup3D +*/ +template class Drawable: public AbstractGroupedFeature, T> { + public: + /** @copydoc AbstractGroupedFeature::AbstractGroupedFeature() */ + inline Drawable(AbstractObject* object, DrawableGroup* group = nullptr): AbstractGroupedFeature, T>(object, group) {} + + /** + * @brief Draw the object using given camera + * @param transformationMatrix %Object transformation relative + * to camera + * @param camera Camera + * + * Projection matrix can be retrieved from AbstractCamera::projectionMatrix(). + */ + virtual void draw(const typename DimensionTraits::MatrixType& transformationMatrix, AbstractCamera* camera) = 0; +}; + +/** +@brief Two-dimensional drawable + +Convenience alternative to %Drawable<2, T>. See Drawable for more +information. +@note Not available on GCC < 4.7. Use %Drawable<2, T> instead. +@see Drawable3D +@todoc Remove workaround when Doxygen supports alias template +*/ +#ifndef DOXYGEN_GENERATING_OUTPUT +#ifndef MAGNUM_GCC46_COMPATIBILITY +template using Drawable2D = Drawable<2, T>; +#endif +#else +typedef Drawable<2, T = GLfloat> Drawable2D; +#endif + +/** +@brief Three-dimensional drawable + +Convenience alternative to %Drawable<3, T>. See Drawable for more +information. +@note Not available on GCC < 4.7. Use %Drawable<3, T> instead. +@see Drawable2D +@todoc Remove workaround when Doxygen supports alias template +*/ +#ifndef DOXYGEN_GENERATING_OUTPUT +#ifndef MAGNUM_GCC46_COMPATIBILITY +template using Drawable3D = Drawable<3, T>; +#endif +#else +typedef Drawable<3, T = GLfloat> Drawable3D; +#endif + +/** +@brief Group of drawables + +See Drawable for more information. +@see DrawableGroup2D, DrawableGroup3D +@todoc Remove workaround when Doxygen supports alias template +*/ +#if !defined(MAGNUM_GCC46_COMPATIBILITY) && !defined(DOXYGEN_GENERATING_OUTPUT) +template using DrawableGroup = FeatureGroup, T>; +#else +template class DrawableGroup: public FeatureGroup, T> {}; +#endif + +/** +@brief Group of two-dimensional drawables + +Convenience alternative to %DrawableGroup<2, T>. See Drawable for +more information. +@note Not available on GCC < 4.7. Use %Drawable<2, T> instead. +@see DrawableGroup3D +@todoc Remove workaround when Doxygen supports alias template +*/ +#ifndef DOXYGEN_GENERATING_OUTPUT +#ifndef MAGNUM_GCC46_COMPATIBILITY +template using DrawableGroup2D = DrawableGroup<2, T>; +#endif +#else +typedef DrawableGroup<2, T = GLfloat> DrawableGroup2D; +#endif + +/** +@brief Group of three-dimensional drawables + +Convenience alternative to %DrawableGroup<3, T>. See Drawable for +more information. +@note Not available on GCC < 4.7. Use %Drawable<3, T> instead. +@see DrawableGroup2D +@todoc Remove workaround when Doxygen supports alias template +*/ +#ifndef DOXYGEN_GENERATING_OUTPUT +#ifndef MAGNUM_GCC46_COMPATIBILITY +template using DrawableGroup3D = DrawableGroup<3, T>; +#endif +#else +typedef DrawableGroup<3, T = GLfloat> DrawableGroup3D; +#endif + +/* Make implementers' life easier */ +#ifndef MAGNUM_GCC46_COMPATIBILITY +template using AbstractCamera2D = AbstractCamera<2, T>; +template using AbstractCamera3D = AbstractCamera<3, T>; +#endif + +}} + +#endif diff --git a/src/SceneGraph/Test/CMakeLists.txt b/src/SceneGraph/Test/CMakeLists.txt index 189382bdf..e0db0910d 100644 --- a/src/SceneGraph/Test/CMakeLists.txt +++ b/src/SceneGraph/Test/CMakeLists.txt @@ -1,2 +1,3 @@ corrade_add_test2(SceneGraphObjectTest ObjectTest.cpp LIBRARIES MagnumSceneGraphTestLib) +corrade_add_test2(SceneGraphCameraTest CameraTest.cpp LIBRARIES MagnumSceneGraph) corrade_add_test2(SceneGraphSceneTest SceneTest.cpp LIBRARIES MagnumSceneGraph) diff --git a/src/SceneGraph/Test/CameraTest.cpp b/src/SceneGraph/Test/CameraTest.cpp index f7a111cf1..36f901ed0 100644 --- a/src/SceneGraph/Test/CameraTest.cpp +++ b/src/SceneGraph/Test/CameraTest.cpp @@ -17,11 +17,21 @@ #include "Math/Constants.h" #include "SceneGraph/Camera.h" +#include "SceneGraph/Camera.hpp" /* only for aspectRatioFix(), so it doesn't have to be exported */ +#include "SceneGraph/Drawable.h" +#include "SceneGraph/MatrixTransformation2D.h" +#include "SceneGraph/MatrixTransformation3D.h" CORRADE_TEST_MAIN(Magnum::SceneGraph::Test::CameraTest) namespace Magnum { namespace SceneGraph { namespace Test { +typedef SceneGraph::Object> Object2D; +typedef SceneGraph::Object> Object3D; +typedef SceneGraph::Scene> Scene3D; +typedef SceneGraph::Camera2D<> Camera2D; +typedef SceneGraph::Camera3D<> Camera3D; + CameraTest::CameraTest() { addTests(&CameraTest::fixAspectRatio, &CameraTest::defaultProjection2D, @@ -29,7 +39,8 @@ CameraTest::CameraTest() { &CameraTest::projection2D, &CameraTest::orthographic, &CameraTest::perspective, - &CameraTest::projectionSizeViewport); + &CameraTest::projectionSizeViewport, + &CameraTest::draw); } void CameraTest::fixAspectRatio() { @@ -41,54 +52,57 @@ void CameraTest::fixAspectRatio() { Vector2 projectionScaleZeroX(0.0f, 0.5f); Math::Vector2 sizeZeroY(400, 0); Math::Vector2 sizeZeroX(0, 300); - CORRADE_COMPARE(Implementation::aspectRatioFix(Implementation::AspectRatioPolicy::Clip, projectionScaleZeroX, size), Matrix4()); - CORRADE_COMPARE(Implementation::aspectRatioFix(Implementation::AspectRatioPolicy::Clip, projectionScaleZeroY, size), Matrix4()); - CORRADE_COMPARE(Implementation::aspectRatioFix(Implementation::AspectRatioPolicy::Clip, projectionScale, sizeZeroY), Matrix4()); - CORRADE_COMPARE(Implementation::aspectRatioFix(Implementation::AspectRatioPolicy::Extend, projectionScale, sizeZeroX), Matrix4()); + CORRADE_COMPARE((Implementation::aspectRatioFix<3, GLfloat>(Implementation::AspectRatioPolicy::Clip, projectionScaleZeroX, size)), Matrix4()); + CORRADE_COMPARE((Implementation::aspectRatioFix<3, GLfloat>(Implementation::AspectRatioPolicy::Clip, projectionScaleZeroY, size)), Matrix4()); + CORRADE_COMPARE((Implementation::aspectRatioFix<3, GLfloat>(Implementation::AspectRatioPolicy::Clip, projectionScale, sizeZeroY)), Matrix4()); + CORRADE_COMPARE((Implementation::aspectRatioFix<3, GLfloat>(Implementation::AspectRatioPolicy::Extend, projectionScale, sizeZeroX)), Matrix4()); /* Not preserved */ - CORRADE_COMPARE(Implementation::aspectRatioFix(Implementation::AspectRatioPolicy::NotPreserved, projectionScale, size), Matrix4()); + CORRADE_COMPARE((Implementation::aspectRatioFix<3, GLfloat>(Implementation::AspectRatioPolicy::NotPreserved, projectionScale, size)), Matrix4()); /* Clip */ Matrix4 expectedClip(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 4.0f/3.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); - CORRADE_COMPARE(Implementation::aspectRatioFix(Implementation::AspectRatioPolicy::Clip, Vector2(0.5f), size), expectedClip); + CORRADE_COMPARE((Implementation::aspectRatioFix<3, GLfloat>(Implementation::AspectRatioPolicy::Clip, Vector2(0.5f), size)), expectedClip); Matrix4 expectedClipRectangle(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); - CORRADE_COMPARE(Implementation::aspectRatioFix(Implementation::AspectRatioPolicy::Clip, projectionScale, size), expectedClipRectangle); + CORRADE_COMPARE((Implementation::aspectRatioFix<3, GLfloat>(Implementation::AspectRatioPolicy::Clip, projectionScale, size)), expectedClipRectangle); /* Extend */ Matrix4 expectedExtend(3.0f/4.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); - CORRADE_COMPARE(Implementation::aspectRatioFix(Implementation::AspectRatioPolicy::Extend, Vector2(0.5f), size), expectedExtend); + CORRADE_COMPARE((Implementation::aspectRatioFix<3, GLfloat>(Implementation::AspectRatioPolicy::Extend, Vector2(0.5f), size)), expectedExtend); Matrix4 expectedExtendRectangle(0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f); - CORRADE_COMPARE(Implementation::aspectRatioFix(Implementation::AspectRatioPolicy::Extend, projectionScale, size), expectedExtendRectangle); + CORRADE_COMPARE((Implementation::aspectRatioFix<3, GLfloat>(Implementation::AspectRatioPolicy::Extend, projectionScale, size)), expectedExtendRectangle); } void CameraTest::defaultProjection2D() { - Camera2D camera; + Object2D o; + Camera2D camera(&o); CORRADE_COMPARE(camera.projectionMatrix(), Matrix3()); CORRADE_COMPARE(camera.projectionSize(), Vector2(2.0f)); } void CameraTest::defaultProjection3D() { - Camera3D camera; + Object3D o; + Camera3D camera(&o); CORRADE_COMPARE(camera.projectionMatrix(), Matrix4()); CORRADE_COMPARE(camera.projectionSize(), Vector2(2.0f)); } void CameraTest::projection2D() { Vector2 projectionSize(4.0f, 3.0f); - Camera2D camera; + Object2D o; + Camera2D camera(&o); camera.setProjection(projectionSize); Matrix3 a(2.0f/4.0f, 0.0f, 0.0f, @@ -101,7 +115,8 @@ void CameraTest::projection2D() { void CameraTest::orthographic() { Vector2 projectionSize(5); - Camera3D camera; + Object3D o; + Camera3D camera(&o); camera.setOrthographic(projectionSize, 1, 9); Matrix4 a(0.4f, 0.0f, 0.0f, 0.0f, @@ -125,7 +140,8 @@ void CameraTest::orthographic() { } void CameraTest::perspective() { - Camera3D camera; + Object3D o; + Camera3D camera(&o); camera.setPerspective(deg(27.0f), 32.0f, 100); Matrix4 a(4.1652994f, 0.0f, 0.0f, 0.0f, @@ -138,7 +154,8 @@ void CameraTest::perspective() { } void CameraTest::projectionSizeViewport() { - Camera3D camera; + Object3D o; + Camera3D camera(&o); camera.setViewport({200, 300}); CORRADE_COMPARE(camera.projectionSize(), Vector2(2.0f, 2.0f)); @@ -149,4 +166,44 @@ void CameraTest::projectionSizeViewport() { CORRADE_COMPARE(camera.projectionSize(), Vector2(4.0f/3.0f, 2.0f)); } +void CameraTest::draw() { + class Drawable: public SceneGraph::Drawable<3> { + public: + inline Drawable(AbstractObject<3>* object, DrawableGroup<3>* group, Matrix4& result): SceneGraph::Drawable<3>(object, group), result(result) {} + + protected: + void draw(const Matrix4& transformationMatrix, AbstractCamera<3>*) { + result = transformationMatrix; + } + + private: + Matrix4& result; + }; + + DrawableGroup<3> group; + Scene3D scene; + + Object3D first(&scene); + Matrix4 firstTransformation; + first.scale(Vector3(5.0f)); + new Drawable(&first, &group, firstTransformation); + + Object3D second(&scene); + Matrix4 secondTransformation; + second.translate(Vector3::yAxis(3.0f)); + new Drawable(&second, &group, secondTransformation); + + Object3D third(&second); + Matrix4 thirdTransformation; + third.translate(Vector3::zAxis(-1.5f)); + new Drawable(&third, &group, thirdTransformation); + + Camera3D camera(&third); + camera.draw(group); + + CORRADE_COMPARE(firstTransformation, Matrix4::translation({0.0f, -3.0f, 1.5f})*Matrix4::scaling(Vector3(5.0f))); + CORRADE_COMPARE(secondTransformation, Matrix4::translation(Vector3::zAxis(1.5f))); + CORRADE_COMPARE(thirdTransformation, Matrix4()); +} + }}} diff --git a/src/SceneGraph/Test/CameraTest.h b/src/SceneGraph/Test/CameraTest.h index 2ada59d3b..1a9ab9198 100644 --- a/src/SceneGraph/Test/CameraTest.h +++ b/src/SceneGraph/Test/CameraTest.h @@ -30,6 +30,7 @@ class CameraTest: public Corrade::TestSuite::Tester { void orthographic(); void perspective(); void projectionSizeViewport(); + void draw(); }; }}}