diff --git a/README.md b/README.md index fdb1f940f..8aa48981c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Magnum is 2D/3D graphics engine written in C++11 and OpenGL 3 Core Profile. +Magnum is 2D/3D graphics engine written in C++11 and modern OpenGL. DESIGN GOALS ============ @@ -34,6 +34,18 @@ DESIGN GOALS done on top of pre-made skeleton classes, support for file formats is done using plugins and platform support is done by writing simple wrapper class. +SUPPORTED PLATFORMS +=================== + +* **OpenGL** 2.1 through 4.3, core profile functionality and modern + extensions +* **OpenGL ES** 2.0, 3.0 and extensions to match desktop OpenGL functionality +* **Linux** and embedded Linux (natively using GLX/EGL and Xlib or through + GLUT or SDL2 toolkit) +* **Windows** (through GLUT or SDL2 toolkit) +* **Google Chrome Native Client** (natively using PPAPI, both `newlib` + and `glibc` toolchains are supported) + FEATURES ======== @@ -46,8 +58,6 @@ FEATURES * Extensible scene graph which can be modified for each specific usage. * Plugin-based data exchange framework, tools for manipulating meshes, textures and images. -* Integration with various windowing toolkits and also ability to create - windowless contexts. * Pre-made shaders, primitives and other tools for easy prototyping and debugging. diff --git a/doc/debug-tools.dox b/doc/debug-tools.dox index 916df7408..afbed3e3e 100644 --- a/doc/debug-tools.dox +++ b/doc/debug-tools.dox @@ -63,7 +63,7 @@ SceneGraph::DrawableGroup3D debugDrawables; // Create renderer options which will be referenced later by "my" resource key DebugTools::ResourceManager::instance()->set("my", - (new DebugTools::ObjectRendererOptions())->setSize(0.3f)); + (new DebugTools::ObjectRendererOptions)->setSize(0.3f)); // Create debug renderer for given object, use "my" options for it. The // renderer is automatically added to the object features and also to diff --git a/doc/mainpage.dox b/doc/mainpage.dox index cc5e6df15..660d85cf6 100644 --- a/doc/mainpage.dox +++ b/doc/mainpage.dox @@ -25,7 +25,7 @@ namespace Magnum { /** @mainpage -%Magnum is 2D/3D graphics engine written in C++11 and OpenGL 3 Core Profile. +%Magnum is 2D/3D graphics engine written in C++11 and modern OpenGL. @section mainpage-design-goals Design goals @@ -60,6 +60,17 @@ namespace Magnum { done on top of pre-made skeleton classes, support for file formats is done using plugins and platform support is done by writing simple wrapper class. +@section mainpage-platforms Supported platforms + +- **OpenGL** 2.1 through 4.3, core profile functionality and modern + extensions +- **OpenGL ES** 2.0, 3.0 and extensions to match desktop OpenGL functionality +- **Linux** and embedded Linux (natively using GLX/EGL and Xlib or through + GLUT or SDL2 toolkit) +- **Windows** (through GLUT or SDL2 toolkit) +- **Google Chrome Native Client** (natively using PPAPI, both `newlib` + and `glibc` toolchains are supported) + @section mainpage-features Features - Vector and matrix library with implementation of complex numbers, @@ -69,8 +80,6 @@ namespace Magnum { - Extensible scene graph which can be modified for each specific usage. - Plugin-based data exchange framework, tools for manipulating meshes, textures and images. -- Integration with various windowing toolkits and also ability to create - windowless contexts. Ported to OpenGL ES and various platforms. - Pre-made shaders, primitives and other tools for easy prototyping and debugging. diff --git a/doc/platform.dox b/doc/platform.dox index 3f82bb0db..525355321 100644 --- a/doc/platform.dox +++ b/doc/platform.dox @@ -147,7 +147,7 @@ window size 800x600 pixels). If you want something else, you can pass constructor. Using method chaining it can be done conveniently like this: @code MyApplication::MyApplication(int& argc, char** argv): - Platform::GlutApplication(argc, argv, (new Configuration()) + Platform::GlutApplication(argc, argv, (new Configuration) ->setTitle("My Application")->setSize({800, 600}) { // ... } @@ -161,7 +161,7 @@ instead of Configuration instance and then specify it later with MyApplication::MyApplication(int& argc, char** argv): Platform::GlutApplication(argc, argv, nullptr) { // ... - createContext((new Configuration()) + createContext((new Configuration) ->setTitle("My Application") ->setSize(size)); @@ -169,5 +169,29 @@ MyApplication::MyApplication(int& argc, char** argv): Platform::GlutApplication( } @endcode +The configuration passed to constructor and @ref GlutApplication::createContext() "createContext()" +is automaticall deleted afterwards and if the context creation fails, the +application exits. However, it is also possible to negotiate the context using +@ref GlutApplication::tryCreateContext() "tryCreateContext()". The major +difference is that this function returns `false` instead of exiting and it +doesn't delete the configuration afterwards so you can reuse it. You can for +example try enabling MSAA and if the context creation fails, fall back to +no-AA rendering: +@code +MyApplication::MyApplication(int& argc, char** argv): Platform::GlutApplication(argc, argv, nullptr) { + // ... + + auto conf = new Configuration; + conf->setTitle("My Application") + ->setSampleCount(16); + + if(!tryCreateContext(conf)) + createContext(conf->setSampleCount(0)); + else delete conf; + + // ... +} +@endcode + */ }} diff --git a/src/DebugTools/CMakeLists.txt b/src/DebugTools/CMakeLists.txt index a80d6e435..bbad443ce 100644 --- a/src/DebugTools/CMakeLists.txt +++ b/src/DebugTools/CMakeLists.txt @@ -33,6 +33,7 @@ set(MagnumDebugTools_SRCS Implementation/AbstractShapeRenderer.cpp Implementation/AxisAlignedBoxRenderer.cpp Implementation/BoxRenderer.cpp + Implementation/LineSegmentRenderer.cpp Implementation/PointRenderer.cpp Implementation/SphereRenderer.cpp) diff --git a/src/DebugTools/ForceRenderer.h b/src/DebugTools/ForceRenderer.h index d9494a212..79e521ab4 100644 --- a/src/DebugTools/ForceRenderer.h +++ b/src/DebugTools/ForceRenderer.h @@ -90,9 +90,8 @@ See @ref debug-tools-renderers for more information. Example code: @code // Create some options -DebugTools::ResourceManager::instance()->set("my", (new DebugTools::ForceRendererOptions() - ->setScale(5.0f) - ->setColor(Color3<>::fromHSV(120.0_degf, 1.0f, 0.7f))); +DebugTools::ResourceManager::instance()->set("my", (new DebugTools::ForceRendererOptions) + ->setScale(5.0f)->setColor(Color3<>::fromHSV(120.0_degf, 1.0f, 0.7f))); // Create debug renderer for given object, use "my" options for it Object3D* object; diff --git a/src/DebugTools/Implementation/LineSegmentRenderer.cpp b/src/DebugTools/Implementation/LineSegmentRenderer.cpp new file mode 100644 index 000000000..c9a0240c8 --- /dev/null +++ b/src/DebugTools/Implementation/LineSegmentRenderer.cpp @@ -0,0 +1,68 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "LineSegmentRenderer.h" + +#include "Mesh.h" +#include "DebugTools/ShapeRenderer.h" +#include "Physics/Line.h" +#include "Primitives/Line.h" +#include "Shaders/FlatShader.h" +#include "Trade/MeshData2D.h" +#include "Trade/MeshData3D.h" + +#include "DebugTools/Implementation/LineSegmentRendererTransformation.h" + +namespace Magnum { namespace DebugTools { namespace Implementation { + +namespace { + template ResourceKey meshKey(); + template<> inline ResourceKey meshKey<2>() { return ResourceKey("line2d"); } + template<> inline ResourceKey meshKey<3>() { return ResourceKey("line3d"); } + + template ResourceKey vertexBufferKey(); + template<> inline ResourceKey vertexBufferKey<2>() { return ResourceKey("line2d-vertices"); } + template<> inline ResourceKey vertexBufferKey<3>() { return ResourceKey("line3d-vertices"); } + + template typename MeshData::Type meshData(); + template<> inline Trade::MeshData2D meshData<2>() { return Primitives::Line2D::wireframe(); } + template<> inline Trade::MeshData3D meshData<3>() { return Primitives::Line3D::wireframe(); } +} + +template LineSegmentRenderer::LineSegmentRenderer(Physics::Line& line): AbstractShapeRenderer(meshKey(), vertexBufferKey(), {}), line(line) { + if(!this->mesh) this->createResources(meshData()); +} + +template void LineSegmentRenderer::draw(Resource& options, const typename DimensionTraits::MatrixType& projectionMatrix) { + this->shader->setTransformationProjectionMatrix(projectionMatrix* + Implementation::lineSegmentRendererTransformation(line.transformedA(), line.transformedB())) + ->setColor(options->color()) + ->use(); + this->mesh->draw(); +} + +template class LineSegmentRenderer<2>; +template class LineSegmentRenderer<3>; + +}}} diff --git a/src/DebugTools/Implementation/LineSegmentRenderer.h b/src/DebugTools/Implementation/LineSegmentRenderer.h new file mode 100644 index 000000000..a67208825 --- /dev/null +++ b/src/DebugTools/Implementation/LineSegmentRenderer.h @@ -0,0 +1,47 @@ +#ifndef Magnum_DebugTools_Implementation_LineSegmentRenderer_h +#define Magnum_DebugTools_Implementation_LineSegmentRenderer_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "AbstractShapeRenderer.h" + +#include "Physics/Physics.h" + +#include "corradeCompatibility.h" + +namespace Magnum { namespace DebugTools { namespace Implementation { + +template class LineSegmentRenderer: public AbstractShapeRenderer { + public: + LineSegmentRenderer(Physics::Line& line); + + void draw(Resource& options, const typename DimensionTraits::MatrixType& projectionMatrix) override; + + private: + Physics::Line& line; +}; + +}}} + +#endif diff --git a/src/DebugTools/Implementation/LineSegmentRendererTransformation.h b/src/DebugTools/Implementation/LineSegmentRendererTransformation.h new file mode 100644 index 000000000..fc94a1bfe --- /dev/null +++ b/src/DebugTools/Implementation/LineSegmentRendererTransformation.h @@ -0,0 +1,39 @@ +#ifndef Magnum_DebugTools_Implementation_LineSegmentRendererTransformation_h +#define Magnum_DebugTools_Implementation_LineSegmentRendererTransformation_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "DimensionTraits.h" + +namespace Magnum { namespace DebugTools { namespace Implementation { + +template typename DimensionTraits::MatrixType lineSegmentRendererTransformation(const typename DimensionTraits::VectorType& a, const typename DimensionTraits::VectorType& b) { + auto transformation = DimensionTraits::MatrixType::translation(a); + transformation.right() = b - a; + return transformation; +} + +}}} + +#endif diff --git a/src/DebugTools/Implementation/SphereRenderer.cpp b/src/DebugTools/Implementation/SphereRenderer.cpp index 67a367c3a..60b27c880 100644 --- a/src/DebugTools/Implementation/SphereRenderer.cpp +++ b/src/DebugTools/Implementation/SphereRenderer.cpp @@ -41,8 +41,8 @@ template SphereRenderer::SphereRenderer(Phys template void SphereRenderer::draw(Resource& options, const typename DimensionTraits::MatrixType& projectionMatrix) { this->shader->setTransformationProjectionMatrix(projectionMatrix* - DimensionTraits::MatrixType::translation(sphere.position())* - DimensionTraits::MatrixType::scaling(typename DimensionTraits::VectorType(sphere.radius()))) + DimensionTraits::MatrixType::translation(sphere.transformedPosition())* + DimensionTraits::MatrixType::scaling(typename DimensionTraits::VectorType(sphere.transformedRadius()))) ->setColor(options->color()) ->use(); this->mesh->draw(); diff --git a/src/DebugTools/ObjectRenderer.h b/src/DebugTools/ObjectRenderer.h index e1c86cab8..cbafafd98 100644 --- a/src/DebugTools/ObjectRenderer.h +++ b/src/DebugTools/ObjectRenderer.h @@ -74,8 +74,8 @@ Visualizes object position, rotation and scale using colored axes. See Example code: @code // Create some options -DebugTools::ResourceManager::instance()->set("my", - (new DebugTools::ObjectRendererOptions())->setSize(0.3f)); +DebugTools::ResourceManager::instance()->set("my", (new DebugTools::ObjectRendererOptions) + ->setSize(0.3f)); // Create debug renderer for given object, use "my" options for it Object3D* object; diff --git a/src/DebugTools/ShapeRenderer.cpp b/src/DebugTools/ShapeRenderer.cpp index b9a072cc5..51a58f93c 100644 --- a/src/DebugTools/ShapeRenderer.cpp +++ b/src/DebugTools/ShapeRenderer.cpp @@ -29,6 +29,7 @@ #include "Physics/AxisAlignedBox.h" #include "Physics/Box.h" #include "Physics/ObjectShape.h" +#include "Physics/LineSegment.h" #include "Physics/Point.h" #include "Physics/ShapeGroup.h" #include "Physics/Sphere.h" @@ -36,6 +37,7 @@ #include "Implementation/AxisAlignedBoxRenderer.h" #include "Implementation/BoxRenderer.h" +#include "Implementation/LineSegmentRenderer.h" #include "Implementation/PointRenderer.h" #include "Implementation/SphereRenderer.h" @@ -52,6 +54,9 @@ template<> void createDebugMesh(ShapeRenderer<2>* renderer, Physics::AbstractSha case Physics::AbstractShape2D::Type::Box: renderer->renderers.push_back(new Implementation::BoxRenderer<2>(*static_cast(shape))); break; + case Physics::AbstractShape2D::Type::LineSegment: + renderer->renderers.push_back(new Implementation::LineSegmentRenderer<2>(*static_cast(shape))); + break; case Physics::AbstractShape2D::Type::Point: renderer->renderers.push_back(new Implementation::PointRenderer<2>(*static_cast(shape))); break; @@ -76,6 +81,9 @@ template<> void createDebugMesh(ShapeRenderer<3>* renderer, Physics::AbstractSha case Physics::AbstractShape3D::Type::Box: renderer->renderers.push_back(new Implementation::BoxRenderer<3>(*static_cast(shape))); break; + case Physics::AbstractShape3D::Type::LineSegment: + renderer->renderers.push_back(new Implementation::LineSegmentRenderer<3>(*static_cast(shape))); + break; case Physics::AbstractShape3D::Type::Point: renderer->renderers.push_back(new Implementation::PointRenderer<3>(*static_cast(shape))); break; diff --git a/src/DebugTools/ShapeRenderer.h b/src/DebugTools/ShapeRenderer.h index 5a73819d3..e1af8bf93 100644 --- a/src/DebugTools/ShapeRenderer.h +++ b/src/DebugTools/ShapeRenderer.h @@ -104,8 +104,8 @@ Visualizes collision shapes using wireframe primitives. See Example code: @code // Create some options -DebugTools::ResourceManager::instance()->set("red", - (new DebugTools::ShapeRendererOptions())->setColor({1.0f, 0.0f, 0.0f})); +DebugTools::ResourceManager::instance()->set("red", (new DebugTools::ShapeRendererOptions) + ->setColor({1.0f, 0.0f, 0.0f})); // Create debug renderer for given shape, use "red" options for it Physics::ObjectShape2D* shape; diff --git a/src/DebugTools/Test/CMakeLists.txt b/src/DebugTools/Test/CMakeLists.txt index 00f3ccf65..1e945d0c0 100644 --- a/src/DebugTools/Test/CMakeLists.txt +++ b/src/DebugTools/Test/CMakeLists.txt @@ -23,3 +23,4 @@ # corrade_add_test(DebugToolsForceRendererTest ForceRendererTest.cpp LIBRARIES MagnumMathTestLib) +corrade_add_test(DebugToolsLineSegmentRendererTest LineSegmentRendererTest.cpp LIBRARIES MagnumMathTestLib) diff --git a/src/DebugTools/Test/LineSegmentRendererTest.cpp b/src/DebugTools/Test/LineSegmentRendererTest.cpp new file mode 100644 index 000000000..5e0a056bc --- /dev/null +++ b/src/DebugTools/Test/LineSegmentRendererTest.cpp @@ -0,0 +1,67 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include "Math/Matrix3.h" +#include "Math/Matrix4.h" +#include "Magnum.h" +#include "DebugTools/Implementation/LineSegmentRendererTransformation.h" + +namespace Magnum { namespace DebugTools { namespace Test { + +class LineSegmentRendererTest: public Corrade::TestSuite::Tester { + public: + explicit LineSegmentRendererTest(); + + void line2D(); + void line3D(); +}; + +LineSegmentRendererTest::LineSegmentRendererTest() { + addTests({&LineSegmentRendererTest::line2D, + &LineSegmentRendererTest::line3D}); +} + +void LineSegmentRendererTest::line2D() { + const Vector2 a(-2.0f, 3.0f); + const Vector2 b(3.4f, -1.5f); + const Matrix3 matrix = Implementation::lineSegmentRendererTransformation<2>(a, b); + + CORRADE_COMPARE(matrix.transformPoint({0.0f, 0.0f}), a); + CORRADE_COMPARE(matrix.transformPoint({1.0f, 0.0f}), b); +} + +void LineSegmentRendererTest::line3D() { + const Vector3 a(-2.0f, 3.0f, 1.5f); + const Vector3 b(3.4f, -1.5f, 0.5f); + const Matrix4 matrix = Implementation::lineSegmentRendererTransformation<3>(a, b); + + CORRADE_COMPARE(matrix.transformPoint({0.0f, 0.0f, 0.0f}), a); + CORRADE_COMPARE(matrix.transformPoint({1.0f, 0.0f, 0.0f}), b); +} + +}}} + +CORRADE_TEST_MAIN(Magnum::DebugTools::Test::LineSegmentRendererTest) diff --git a/src/Math/Algorithms/Svd.h b/src/Math/Algorithms/Svd.h index 1c239efc1..e86d68265 100644 --- a/src/Math/Algorithms/Svd.h +++ b/src/Math/Algorithms/Svd.h @@ -49,7 +49,7 @@ template T pythagoras(T a, T b) { return absb*std::sqrt(T(1) + Math::pow<2>(absa/absb)); } -template T smallestDelta(); +template constexpr T smallestDelta(); template<> inline constexpr Float smallestDelta() { return 1.0e-32; } #ifndef MAGNUM_TARGET_GLES template<> inline constexpr Double smallestDelta() { return 1.0e-64; } diff --git a/src/Math/Functions.h b/src/Math/Functions.h index 4da048141..b2625be6f 100644 --- a/src/Math/Functions.h +++ b/src/Math/Functions.h @@ -164,8 +164,7 @@ template Vector max(const Vector& a /** @brief Sign -Returns `1.0` if @f$ x > 0 @f$, `0.0` if @f$ @p x = 0 @f$ and `-1.0` if -@f$ x < 0 @f$. +Returns `1` if @p x > 0, `0` if @p x = 0 and `-1` if @p x < 0. */ #ifdef DOXYGEN_GENERATING_OUTPUT template inline T sign(const T scalar); diff --git a/src/Math/Geometry/Rectangle.h b/src/Math/Geometry/Rectangle.h index ccf8ac019..6211bd8b7 100644 --- a/src/Math/Geometry/Rectangle.h +++ b/src/Math/Geometry/Rectangle.h @@ -60,7 +60,7 @@ template class Rectangle { * * Construct zero-area rectangle positioned at origin. */ - inline constexpr Rectangle() = default; + inline constexpr Rectangle() {} /** @brief Construct rectangle from two corners */ inline constexpr Rectangle(const Vector2& bottomLeft, const Vector2& topRight): _bottomLeft(bottomLeft), _topRight(topRight) {} diff --git a/src/Math/Test/Vector2Test.cpp b/src/Math/Test/Vector2Test.cpp index 4eccd3802..6364b2e71 100644 --- a/src/Math/Test/Vector2Test.cpp +++ b/src/Math/Test/Vector2Test.cpp @@ -26,7 +26,7 @@ #include #include -#include "Math/Vector2.h" +#include "Math/Vector3.h" struct Vec2 { float x, y; @@ -63,13 +63,16 @@ class Vector2Test: public Corrade::TestSuite::Tester { void convert(); void access(); + void cross(); void axes(); void scales(); + void perpendicular(); void debug(); void configuration(); }; +typedef Math::Vector3 Vector3i; typedef Math::Vector2 Vector2; typedef Math::Vector2 Vector2i; @@ -83,8 +86,10 @@ Vector2Test::Vector2Test() { &Vector2Test::convert, &Vector2Test::access, + &Vector2Test::cross, &Vector2Test::axes, &Vector2Test::scales, + &Vector2Test::perpendicular, &Vector2Test::debug, &Vector2Test::configuration}); @@ -167,6 +172,14 @@ void Vector2Test::access() { CORRADE_COMPARE(y, -2.0f); } +void Vector2Test::cross() { + Vector2i a(1, -1); + Vector2i b(4, 3); + + CORRADE_COMPARE(Vector2i::cross(a, b), 7); + CORRADE_COMPARE(Vector3i::cross({a, 0}, {b, 0}), Vector3i(0, 0, Vector2i::cross(a, b))); +} + void Vector2Test::axes() { constexpr Vector2 x = Vector2::xAxis(5.0f); constexpr Vector2 y = Vector2::yAxis(6.0f); @@ -181,6 +194,13 @@ void Vector2Test::scales() { CORRADE_COMPARE(y, Vector2(1.0f, -0.2f)); } +void Vector2Test::perpendicular() { + const Vector2 a(0.5f, -15.0f); + CORRADE_COMPARE(a.perpendicular(), Vector2(15.0f, 0.5f)); + CORRADE_COMPARE(Vector2::dot(a.perpendicular(), a), 0.0f); + CORRADE_COMPARE(Vector2::xAxis().perpendicular(), Vector2::yAxis()); +} + void Vector2Test::debug() { std::ostringstream o; Debug(&o) << Vector2(0.5f, 15.0f); diff --git a/src/Math/Test/Vector3Test.cpp b/src/Math/Test/Vector3Test.cpp index 439fdecb9..5cf8ffd32 100644 --- a/src/Math/Test/Vector3Test.cpp +++ b/src/Math/Test/Vector3Test.cpp @@ -185,10 +185,10 @@ void Vector3Test::access() { } void Vector3Test::cross() { - Vector3 a(1, -1, 1); - Vector3 b(4, 3, 7); + Vector3i a(1, -1, 1); + Vector3i b(4, 3, 7); - CORRADE_COMPARE(Vector3::cross(a, b), Vector3(-10, -3, 7)); + CORRADE_COMPARE(Vector3i::cross(a, b), Vector3i(-10, -3, 7)); } void Vector3Test::axes() { diff --git a/src/Math/Vector.h b/src/Math/Vector.h index a773b73a8..fa25328d6 100644 --- a/src/Math/Vector.h +++ b/src/Math/Vector.h @@ -84,10 +84,12 @@ template class Vector { /** * @brief Dot product * - * @f[ + * Returns `0` if two vectors are orthogonal, `1` if two *normalized* + * vectors are parallel and `-1` if two *normalized* vectors are + * antiparallel. @f[ * \boldsymbol a \cdot \boldsymbol b = \sum_{i=0}^{n-1} \boldsymbol a_i \boldsymbol b_i * @f] - * @see dot() const + * @see dot() const, operator-(), Vector2::perpendicular() */ inline static T dot(const Vector& a, const Vector& b) { return (a*b).sum(); @@ -278,6 +280,7 @@ template class Vector { * The computation is done in-place. @f[ * \boldsymbol a_i = -\boldsymbol a_i * @f] + * @see Vector2::perpendicular() */ Vector operator-() const { Vector out; diff --git a/src/Math/Vector2.h b/src/Math/Vector2.h index acab7d8d2..3a3e549f4 100644 --- a/src/Math/Vector2.h +++ b/src/Math/Vector2.h @@ -80,6 +80,20 @@ template class Vector2: public Vector<2, T> { */ inline constexpr static Vector2 yScale(T scale) { return Vector2(T(1), scale); } + /** + * @brief 2D cross product + * + * 2D version of cross product, equivalent to calling Vector3::cross() + * with Z coordinate set to `0` and extracting only Z coordinate from + * the result (X and Y coordinates are always zero). + * @f[ + * \boldsymbol a \times \boldsymbol b = a_xb_y - a_yb_x + * @f] + */ + inline static T cross(const Vector2& a, const Vector2& b) { + return a.x()*b.y() - a.y()*b.x(); + } + /** @copydoc Vector::Vector() */ inline constexpr /*implicit*/ Vector2() {} @@ -113,6 +127,16 @@ template class Vector2: public Vector<2, T> { inline T& y() { return (*this)[1]; } /**< @brief Y component */ inline constexpr T y() const { return (*this)[1]; } /**< @overload */ + /** + * @brief Perpendicular vector + * + * Returns vector rotated 90° counterclockwise. @f[ + * \boldsymbol v_\perp = \begin{pmatrix} -v_y \\ v_x \end{pmatrix} + * @f] + * @see dot(const Vector&, const Vector&), operator-() const + */ + inline Vector2 perpendicular() const { return {-y(), x()}; } + MAGNUM_VECTOR_SUBCLASS_IMPLEMENTATION(Vector2, 2) }; diff --git a/src/Math/Vector3.h b/src/Math/Vector3.h index 3dd0aa127..e936efb8c 100644 --- a/src/Math/Vector3.h +++ b/src/Math/Vector3.h @@ -102,12 +102,12 @@ template class Vector3: public Vector<3, T> { * @brief Cross product * * @f[ - * \boldsymbol a \times \boldsymbol b = - * \begin{pmatrix} c_0 \\ c_1 \\ c_2 \end{pmatrix} = - * \begin{pmatrix}a_1b_2 - a_2b_1 \\ a_2b_0 - a_0b_2 \\ a_0b_1 - a_1b_0 \end{pmatrix} + * \boldsymbol a \times \boldsymbol b = + * \begin{pmatrix}a_yb_z - a_zb_y \\ a_zb_y - a_xb_z \\ a_xb_y - a_yb_x \end{pmatrix} * @f] + * @see Vector2::cross() */ - inline constexpr static Vector3 cross(const Vector3& a, const Vector3& b) { + inline static Vector3 cross(const Vector3& a, const Vector3& b) { return swizzle<'y', 'z', 'x'>(a)*swizzle<'z', 'x', 'y'>(b) - swizzle<'z', 'x', 'y'>(a)*swizzle<'y', 'z', 'x'>(b); } diff --git a/src/Platform/AbstractXApplication.cpp b/src/Platform/AbstractXApplication.cpp index 153429d13..80e6e7215 100644 --- a/src/Platform/AbstractXApplication.cpp +++ b/src/Platform/AbstractXApplication.cpp @@ -50,7 +50,7 @@ void AbstractXApplication::createContext(AbstractXApplication::Configuration* co viewportSize = configuration->size(); /* Get default X display */ - display = XOpenDisplay(0); + display = XOpenDisplay(nullptr); /* Get visual ID */ VisualID visualId = contextHandler->getVisualId(display); @@ -62,7 +62,7 @@ void AbstractXApplication::createContext(AbstractXApplication::Configuration* co visInfo = XGetVisualInfo(display, VisualIDMask, &visTemplate, &visualCount); if(!visInfo) { Error() << "Cannot get X visual"; - ::exit(1); + std::exit(1); } /* Create X Window */ @@ -74,7 +74,7 @@ void AbstractXApplication::createContext(AbstractXApplication::Configuration* co attr.event_mask = 0; unsigned long mask = CWBackPixel|CWBorderPixel|CWColormap|CWEventMask; window = XCreateWindow(display, root, 20, 20, configuration->size().x(), configuration->size().y(), 0, visInfo->depth, InputOutput, visInfo->visual, mask, &attr); - XSetStandardProperties(display, window, configuration->title().c_str(), 0, None, 0, 0, 0); + XSetStandardProperties(display, window, configuration->title().c_str(), nullptr, None, nullptr, 0, nullptr); XFree(visInfo); /* Be notified about closing the window */ diff --git a/src/Platform/GlutApplication.cpp b/src/Platform/GlutApplication.cpp index 38741a3fb..705264312 100644 --- a/src/Platform/GlutApplication.cpp +++ b/src/Platform/GlutApplication.cpp @@ -47,12 +47,20 @@ void GlutApplication::initialize(int& argc, char** argv) { /* Init GLUT */ glutInit(&argc, argv); + glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION); } void GlutApplication::createContext(Configuration* configuration) { - CORRADE_ASSERT(!c, "GlutApplication::createContext(): context already created", ); + if(!tryCreateContext(configuration)) { + Error() << "Platform::GlutApplication::createContext(): cannot create context"; + delete configuration; + std::exit(1); - glutSetOption(GLUT_ACTION_ON_WINDOW_CLOSE, GLUT_ACTION_CONTINUE_EXECUTION); + } else delete configuration; +} + +bool GlutApplication::tryCreateContext(Configuration* configuration) { + CORRADE_ASSERT(!c, "Platform::GlutApplication::tryCreateContext(): context already created", false); unsigned int flags = GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL; @@ -61,7 +69,8 @@ void GlutApplication::createContext(Configuration* configuration) { glutInitDisplayMode(flags); glutInitWindowSize(configuration->size().x(), configuration->size().y()); - glutCreateWindow(configuration->title().c_str()); + if(!glutCreateWindow(configuration->title().c_str())) + return false; glutReshapeFunc(staticViewportEvent); glutSpecialFunc(staticKeyEvent); glutMouseFunc(staticMouseEvent); @@ -71,7 +80,7 @@ void GlutApplication::createContext(Configuration* configuration) { ExtensionWrangler::initialize(); c = new Context; - delete configuration; + return true; } GlutApplication::~GlutApplication() { diff --git a/src/Platform/GlutApplication.h b/src/Platform/GlutApplication.h index ce141930b..4987ccf60 100644 --- a/src/Platform/GlutApplication.h +++ b/src/Platform/GlutApplication.h @@ -81,7 +81,8 @@ class GlutApplication { * @param arguments Application arguments * * Creates application with default configuration. See Configuration - * for more information. + * for more information. The program exits if the context cannot be + * created, see tryCreateContext() for an alternative. */ explicit GlutApplication(const Arguments& arguments); @@ -92,7 +93,8 @@ class GlutApplication { * * The @p configuration is deleted afterwards. If `nullptr` is passed * as @p configuration, the context is not created and must be created - * with createContext(). + * with createContext(). The program exits if the context cannot be + * created, see tryCreateContext() for an alternative. */ explicit GlutApplication(const Arguments& arguments, Configuration* configuration); @@ -112,10 +114,21 @@ class GlutApplication { * @brief Create context with given configuration * * The @p configuration is deleted afterwards. Must be called if and - * only if the context wasn't created by the constructor itself. + * only if the context wasn't created by the constructor itself. The + * program exits if the context cannot be created, see tryCreateContext() + * for an alternative. */ void createContext(Configuration* configuration); + /** + * @brief Try to create context with given configuration + * + * Unlike createContext() the @p configuration is *not* deleted + * afterwards. Returns `false` if the context cannot be created, `true` + * otherwise. + */ + bool tryCreateContext(Configuration* configuration); + /** @{ @name Drawing functions */ /** diff --git a/src/Platform/GlxContextHandler.cpp b/src/Platform/GlxContextHandler.cpp index f39cd2db4..9e7506424 100644 --- a/src/Platform/GlxContextHandler.cpp +++ b/src/Platform/GlxContextHandler.cpp @@ -84,7 +84,7 @@ void GlxContextHandler::createContext(Window nativeWindow) { /** @todo Use some extension wrangler for this, not GLEW, as it apparently needs context to create context, yo dawg wtf. */ PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB"); - context = glXCreateContextAttribsARB(display, configs[0], 0, True, attributes); + context = glXCreateContextAttribsARB(display, configs[0], nullptr, True, attributes); XFree(configs); if(!context) { Error() << "GlxContextHandler: cannot create context."; diff --git a/src/Platform/NaClApplication.cpp b/src/Platform/NaClApplication.cpp index 4b82e6e6f..504d6d750 100644 --- a/src/Platform/NaClApplication.cpp +++ b/src/Platform/NaClApplication.cpp @@ -36,16 +36,25 @@ NaClApplication::NaClApplication(const Arguments& arguments): Instance(arguments createContext(new Configuration); } -NaClApplication::NaClApplication(const Arguments& arguments, Configuration* configuration): Instance(arguments), Graphics3DClient(this), MouseLock(this), c(nullptr) { +NaClApplication::NaClApplication(const Arguments& arguments, Configuration* configuration): Instance(arguments), Graphics3DClient(this), MouseLock(this), graphics(nullptr), fullscreen(nullptr), c(nullptr) { if(configuration) createContext(configuration); } -void NaClApplication::createContext(NaClApplication::Configuration* configuration) { - CORRADE_ASSERT(!c, "NaClApplication::createContext(): context already created", ); +void NaClApplication::createContext(Configuration* configuration) { + if(!tryCreateContext(configuration)) { + Error() << "Platform::NaClApplication::createContext(): cannot create context"; + delete configuration; + std::exit(1); + + } else delete configuration; +} + +bool NaClApplication::tryCreateContext(Configuration* configuration) { + CORRADE_ASSERT(!c, "Platform::NaClApplication::tryCreateContext(): context already created", false); viewportSize = configuration->size(); - std::int32_t attributes[] = { + const std::int32_t attributes[] = { PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8, PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 24, PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 8, @@ -58,11 +67,12 @@ void NaClApplication::createContext(NaClApplication::Configuration* configuratio graphics = new pp::Graphics3D(this, attributes); if(graphics->is_null()) { - Error() << "Platform::NaClApplication::NaClApplication(): cannot create graphics"; - std::exit(1); + delete graphics; + graphics = nullptr; + return false; } if(!BindGraphics(*graphics)) { - Error() << "Platform::NaClApplication::NaClApplication(): cannot bind graphics"; + Error() << "Platform::NaClApplication::tryCreateContext(): cannot bind graphics"; std::exit(1); } @@ -70,8 +80,6 @@ void NaClApplication::createContext(NaClApplication::Configuration* configuratio glSetCurrentContextPPAPI(graphics->pp_resource()); - c = new Context; - /* Enable input handling for mouse and keyboard */ RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE|PP_INPUTEVENT_CLASS_WHEEL); RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD); @@ -79,7 +87,8 @@ void NaClApplication::createContext(NaClApplication::Configuration* configuratio /* Make sure viewportEvent() is called for first time */ flags |= Flag::ViewportUpdated; - delete configuration; + c = new Context; + return true; } NaClApplication::~NaClApplication() { diff --git a/src/Platform/NaClApplication.h b/src/Platform/NaClApplication.h index 46419c989..ac7e119f2 100644 --- a/src/Platform/NaClApplication.h +++ b/src/Platform/NaClApplication.h @@ -121,6 +121,9 @@ class NaClApplication: public pp::Instance, public pp::Graphics3DClient, public /** @copydoc GlutApplication::createContext() */ void createContext(Configuration* configuration); + /** @copydoc GlutApplication::tryCreateContext() */ + bool tryCreateContext(Configuration* configuration); + /** @{ @name Drawing functions */ /** @copydoc GlutApplication::viewportEvent() */ diff --git a/src/Platform/Sdl2Application.cpp b/src/Platform/Sdl2Application.cpp index d5f9a1850..d5e5b3855 100644 --- a/src/Platform/Sdl2Application.cpp +++ b/src/Platform/Sdl2Application.cpp @@ -50,39 +50,59 @@ Sdl2Application::InputEvent::Modifiers fixedModifiers(Uint16 mod) { } Sdl2Application::Sdl2Application(const Arguments&): context(nullptr), flags(Flag::Redraw) { + initialize(); createContext(new Configuration); } Sdl2Application::Sdl2Application(const Arguments&, Configuration* configuration): context(nullptr), flags(Flag::Redraw) { + initialize(); if(configuration) createContext(configuration); } -void Sdl2Application::createContext(Configuration* configuration) { - CORRADE_ASSERT(!context, "Sdl2Application::createContext(): context already created", ); - +void Sdl2Application::initialize() { if(SDL_Init(SDL_INIT_VIDEO) < 0) { Error() << "Cannot initialize SDL."; std::exit(1); } +} + +void Sdl2Application::createContext(Configuration* configuration) { + if(!tryCreateContext(configuration)) { + Error() << "Platform::Sdl2Application::createContext(): cannot create context:" << SDL_GetError(); + delete configuration; + std::exit(1); + + } else delete configuration; +} + +bool Sdl2Application::tryCreateContext(Configuration* configuration) { + CORRADE_ASSERT(!context, "Platform::Sdl2Application::tryCreateContext(): context already created", false); /* Enable double buffering and 24bt depth buffer */ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); /* Multisampling */ - if(configuration->sampleCount()) { - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, configuration->sampleCount()); - } - - window = SDL_CreateWindow(configuration->title().c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, - configuration->size().x(), configuration->size().y(), SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN); - if(!window) { - Error() << "Cannot create window."; + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, configuration->sampleCount() > 1 ? 1 : 0); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, configuration->sampleCount()); + + /* Flags: if not hidden, set as shown */ + Uint32 flags(configuration->flags()); + if(!(configuration->flags() & Configuration::Flag::Hidden)) flags |= SDL_WINDOW_SHOWN; + + if(!(window = SDL_CreateWindow(configuration->title().c_str(), + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + configuration->size().x(), configuration->size().y(), + SDL_WINDOW_OPENGL|flags))) { + Error() << "Platform::Sdl2Application::tryCreateContext(): cannot create window"; std::exit(2); } - context = SDL_GL_CreateContext(window); + if(!(context = SDL_GL_CreateContext(window))) { + SDL_DestroyWindow(window); + window = nullptr; + return false; + } /* This must be enabled, otherwise (on my NVidia) it crashes when creating VAO. WTF. */ @@ -97,7 +117,7 @@ void Sdl2Application::createContext(Configuration* configuration) { SDL_PushEvent(sizeEvent); c = new Context; - delete configuration; + return true; } Sdl2Application::~Sdl2Application() { @@ -167,7 +187,7 @@ void Sdl2Application::setMouseLocked(bool enabled) { SDL_SetRelativeMouseMode(enabled ? SDL_TRUE : SDL_FALSE); } -Sdl2Application::Configuration::Configuration(): _title("Magnum SDL2 Application"), _size(800, 600), _sampleCount(0) {} +Sdl2Application::Configuration::Configuration(): _title("Magnum SDL2 Application"), _size(800, 600), _flags(Flag::Resizable), _sampleCount(0) {} Sdl2Application::Configuration::~Configuration() = default; Sdl2Application::InputEvent::Modifiers Sdl2Application::MouseEvent::modifiers() { diff --git a/src/Platform/Sdl2Application.h b/src/Platform/Sdl2Application.h index fb9a5f0e7..5ed06ed7a 100644 --- a/src/Platform/Sdl2Application.h +++ b/src/Platform/Sdl2Application.h @@ -95,6 +95,9 @@ class Sdl2Application { /** @copydoc GlutApplication::createContext() */ void createContext(Configuration* configuration); + /** @copydoc GlutApplication::tryCreateContext() */ + bool tryCreateContext(Configuration* configuration); + /** @{ @name Drawing functions */ /** @copydoc GlutApplication::viewportEvent() */ @@ -167,6 +170,8 @@ class Sdl2Application { typedef Corrade::Containers::EnumSet Flags; CORRADE_ENUMSET_FRIEND_OPERATORS(Flags) + void initialize(); + SDL_Window* window; SDL_GLContext context; @@ -191,6 +196,29 @@ class Sdl2Application::Configuration { Configuration& operator=(Configuration&&) = delete; public: + /** + * @brief Window flag + * + * @see Flags, setFlags() + */ + enum class Flag: Uint32 { + Resizable = SDL_WINDOW_RESIZABLE, /**< Resizable window */ + Fullscreen = SDL_WINDOW_FULLSCREEN, /**< Fullscreen window */ + Hidden = SDL_WINDOW_HIDDEN, /**< Hidden window */ + Maximized = SDL_WINDOW_MAXIMIZED, /**< Maximized window */ + Minimized = SDL_WINDOW_MINIMIZED, /**< Minimized window */ + MouseLocked = SDL_WINDOW_INPUT_GRABBED /**< Window with mouse locked */ + }; + + /** + * @brief Window flags + * + * @see setFlags() + */ + typedef Corrade::Containers::EnumSet Flags; + explicit Configuration(); ~Configuration(); @@ -222,6 +250,20 @@ class Sdl2Application::Configuration { return this; } + /** @brief Window flags */ + inline Flags flags() const { return _flags; } + + /** + * @brief Set window flags + * @return Pointer to self (for method chaining) + * + * Default is @ref Flag "Flag::Resizable". + */ + inline Configuration* setFlags(const Flags flags) { + _flags = flags; + return this; + } + /** @brief Sample count */ inline Int sampleCount() const { return _sampleCount; } @@ -240,9 +282,12 @@ class Sdl2Application::Configuration { private: std::string _title; Vector2i _size; + Flags _flags; Int _sampleCount; }; +CORRADE_ENUMSET_OPERATORS(Sdl2Application::Configuration::Flags) + /** @brief Base for input events diff --git a/src/Platform/WindowlessGlxApplication.cpp b/src/Platform/WindowlessGlxApplication.cpp index 39f934f60..2d7c017b8 100644 --- a/src/Platform/WindowlessGlxApplication.cpp +++ b/src/Platform/WindowlessGlxApplication.cpp @@ -74,7 +74,7 @@ void WindowlessGlxApplication::createContext(Configuration* configuration) { /** @todo Use some extension wrangler for this, not GLEW, as it apparently needs context to create context, yo dawg wtf. */ PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB"); - context = glXCreateContextAttribsARB(display, configs[0], 0, True, contextAttributes); + context = glXCreateContextAttribsARB(display, configs[0], nullptr, True, contextAttributes); if(!context) { Error() << "WindowlessGlxApplication: cannot create context."; std::exit(1); diff --git a/src/Primitives/CMakeLists.txt b/src/Primitives/CMakeLists.txt index 6c44f1c81..9675469aa 100644 --- a/src/Primitives/CMakeLists.txt +++ b/src/Primitives/CMakeLists.txt @@ -29,6 +29,7 @@ set(MagnumPrimitives_SRCS Cube.cpp Cylinder.cpp Icosphere.cpp + Line.cpp Plane.cpp Square.cpp UVSphere.cpp) @@ -40,6 +41,7 @@ set(MagnumPrimitives_HEADERS Cube.h Cylinder.h Icosphere.h + Line.h Plane.h Square.h UVSphere.h diff --git a/src/Primitives/Line.cpp b/src/Primitives/Line.cpp new file mode 100644 index 000000000..53926a9f6 --- /dev/null +++ b/src/Primitives/Line.cpp @@ -0,0 +1,45 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "Line.h" + +#include "Math/Vector3.h" +#include "Trade/MeshData2D.h" +#include "Trade/MeshData3D.h" + +namespace Magnum { namespace Primitives { + +Trade::MeshData2D Line2D::wireframe() { + return Trade::MeshData2D(Mesh::Primitive::Lines, nullptr, {new std::vector{ + {0.0f, 0.0f}, {1.0f, 0.0f} + }}, {}); +} + +Trade::MeshData3D Line3D::wireframe() { + return Trade::MeshData3D(Mesh::Primitive::Lines, nullptr, {new std::vector{ + {0.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 0.0f}, + }}, {}, {}); +} + +}} diff --git a/src/Primitives/Line.h b/src/Primitives/Line.h new file mode 100644 index 000000000..59a0d7a6c --- /dev/null +++ b/src/Primitives/Line.h @@ -0,0 +1,67 @@ +#ifndef Magnum_Primitives_Line_h +#define Magnum_Primitives_Line_h +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/** @file + * @brief Class Magnum::Primitives::Line2D, Magnum::Primitives::Line3D + */ + +#include "Trade/Trade.h" + +#include "Primitives/magnumPrimitivesVisibility.h" + +namespace Magnum { namespace Primitives { + +/** +@brief 2D line primitive + +Unit-size line in direction of positive X axis. Non-indexed +@ref Mesh::Primitive "Lines". +*/ +class MAGNUM_PRIMITIVES_EXPORT Line2D { + public: + /** @brief Wireframe line */ + static Trade::MeshData2D wireframe(); + + Line2D() = delete; +}; + +/** +@brief 3D line primitive + +Unit-size line in direction of positive X axis. Non-indexed +@ref Mesh::Primitive "Lines". +*/ +class MAGNUM_PRIMITIVES_EXPORT Line3D { + public: + /** @brief Wireframe line */ + static Trade::MeshData3D wireframe(); + + Line3D() = delete; +}; + +}} + +#endif diff --git a/src/SceneGraph/Object.hpp b/src/SceneGraph/Object.hpp index cd35d9da4..be80d9c4c 100644 --- a/src/SceneGraph/Object.hpp +++ b/src/SceneGraph/Object.hpp @@ -180,8 +180,6 @@ template std::vector Ob /* Remember object count for later */ std::size_t objectCount = objects.size(); - /** @bug What if there is one objects twice in the list */ - /* Mark all original objects as joints and create initial list of joints from them */ for(std::size_t i = 0; i != objects.size(); ++i) { @@ -323,6 +321,22 @@ template void Object::setClean(std::vector /* No dirty objects left, done */ if(objects.empty()) return; + /* Add non-clean parents to the list. Mark each added object as visited, so + they aren't added more than once */ + for(std::size_t end = objects.size(), i = 0; i != end; ++i) { + Object* o = objects[i]; + o->flags |= Flag::Visited; + + Object* parent = o->parent(); + while(parent && !(parent->flags & Flag::Visited) && parent->isDirty()) { + objects.push_back(parent); + parent = parent->parent(); + } + } + + /* Cleanup all marks */ + for(auto o: objects) o->flags &= ~Flag::Visited; + /* Compute absolute transformations */ Scene* scene = objects[0]->scene(); CORRADE_ASSERT(scene, "Object::setClean(): objects must be part of some scene", ); diff --git a/src/SceneGraph/Test/ObjectTest.cpp b/src/SceneGraph/Test/ObjectTest.cpp index 51f1187f2..a54acdd43 100644 --- a/src/SceneGraph/Test/ObjectTest.cpp +++ b/src/SceneGraph/Test/ObjectTest.cpp @@ -42,7 +42,8 @@ class ObjectTest: public Corrade::TestSuite::Tester { void transformationsOrphan(); void transformationsDuplicate(); void setClean(); - void bulkSetClean(); + void setCleanListHierarchy(); + void setCleanListBulk(); }; typedef SceneGraph::Object> Object3D; @@ -71,7 +72,8 @@ ObjectTest::ObjectTest() { &ObjectTest::transformationsOrphan, &ObjectTest::transformationsDuplicate, &ObjectTest::setClean, - &ObjectTest::bulkSetClean}); + &ObjectTest::setCleanListHierarchy, + &ObjectTest::setCleanListBulk}); } void ObjectTest::parenting() { @@ -305,10 +307,13 @@ void ObjectTest::setClean() { /* If the object itself is already clean, it shouldn't clean it again */ childOne->cleanedAbsoluteTransformation = Matrix4(Matrix4::Zero); + CORRADE_VERIFY(!childOne->isDirty()); childOne->setClean(); CORRADE_COMPARE(childOne->cleanedAbsoluteTransformation, Matrix4(Matrix4::Zero)); /* If any object in the hierarchy is already clean, it shouldn't clean it again */ + CORRADE_VERIFY(!childOne->isDirty()); + CORRADE_VERIFY(childTwo->isDirty()); childTwo->setClean(); CORRADE_COMPARE(childOne->cleanedAbsoluteTransformation, Matrix4(Matrix4::Zero)); @@ -332,7 +337,58 @@ void ObjectTest::setClean() { CORRADE_VERIFY(childThree->isDirty()); } -void ObjectTest::bulkSetClean() { +void ObjectTest::setCleanListHierarchy() { + Scene3D scene; + + class CachingFeature: public AbstractFeature<3> { + public: + CachingFeature(AbstractObject<3>* object): AbstractFeature<3>(object) { + setCachedTransformations(CachedTransformation::Absolute); + } + + Matrix4 cleanedAbsoluteTransformation; + + void clean(const Matrix4& absoluteTransformation) override { + cleanedAbsoluteTransformation = absoluteTransformation; + } + }; + + CachingObject* childOne = new CachingObject(&scene); + childOne->scale(Vector3(2.0f)); + + CachingObject* childTwo = new CachingObject(childOne); + childTwo->translate(Vector3::xAxis(1.0f)); + CachingFeature* childTwoFeature = new CachingFeature(childTwo); + + CachingObject* childThree = new CachingObject(childTwo); + childThree->rotate(Deg(90.0f), Vector3::yAxis()); + + /* Clean the object and all its dirty parents (but not children) */ + Scene3D::setClean(std::vector{childTwo}); + CORRADE_VERIFY(!scene.isDirty()); + CORRADE_VERIFY(!childOne->isDirty()); + CORRADE_VERIFY(!childTwo->isDirty()); + CORRADE_VERIFY(childThree->isDirty()); + + /* Verify the right matrices were passed */ + CORRADE_COMPARE(childOne->cleanedAbsoluteTransformation, childOne->absoluteTransformationMatrix()); + CORRADE_COMPARE(childTwo->cleanedAbsoluteTransformation, childTwo->absoluteTransformationMatrix()); + CORRADE_COMPARE(childTwoFeature->cleanedAbsoluteTransformation, childTwo->absoluteTransformationMatrix()); + + /* If the object itself is already clean, it shouldn't clean it again */ + childOne->cleanedAbsoluteTransformation = Matrix4(Matrix4::Zero); + CORRADE_VERIFY(!childOne->isDirty()); + Scene3D::setClean(std::vector{childOne}); + CORRADE_COMPARE(childOne->cleanedAbsoluteTransformation, Matrix4(Matrix4::Zero)); + + /* If any object in the hierarchy is already clean, it shouldn't clean it again */ + CORRADE_VERIFY(!childOne->isDirty()); + childTwo->setDirty(); + Scene3D::setClean(std::vector{childTwo}); + CORRADE_COMPARE(childOne->cleanedAbsoluteTransformation, Matrix4(Matrix4::Zero)); +} + +void ObjectTest::setCleanListBulk() { /* Verify it doesn't crash when passed empty list */ Object3D::setClean(std::vector()); diff --git a/src/Timeline.h b/src/Timeline.h index 8b0426ea1..4fde2a561 100644 --- a/src/Timeline.h +++ b/src/Timeline.h @@ -58,10 +58,9 @@ performed, after everything is properly initialized. In your draw event implementation don't forget to call nextFrame() after buffer swap. You can use previousFrameDuration() to compute animation speed. -Example usage (in e.g. @ref Platform::GlutApplication "GlutApplication" -subclass): +Example usage: @code -MyApplication::MyApplication(...): Platform::GlutApplication(...) { +MyApplication::MyApplication(const Parameters& parameters): Platform::Application(parameters) { // Initialization ... timeline.setMinimalFrameTime(1/120.0f); // 120 FPS at max