diff --git a/src/DebugTools/CMakeLists.txt b/src/DebugTools/CMakeLists.txt index 8d6dbdbc4..6e91b9f05 100644 --- a/src/DebugTools/CMakeLists.txt +++ b/src/DebugTools/CMakeLists.txt @@ -1,4 +1,5 @@ set(MagnumDebugTools_SRCS + ForceRenderer.cpp ObjectRenderer.cpp Profiler.cpp ResourceManager.cpp @@ -11,6 +12,7 @@ set(MagnumDebugTools_SRCS Implementation/PointRenderer.cpp) set(MagnumDebugTools_HEADERS + ForceRenderer.h DebugTools.h ObjectRenderer.h Profiler.h @@ -30,3 +32,7 @@ target_link_libraries(MagnumDebugTools install(TARGETS MagnumDebugTools DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR}) install(FILES ${MagnumDebugTools_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/DebugTools) + +if(BUILD_TESTS) + add_subdirectory(Test) +endif() diff --git a/src/DebugTools/DebugTools.h b/src/DebugTools/DebugTools.h index 1cac85e90..15f1e786b 100644 --- a/src/DebugTools/DebugTools.h +++ b/src/DebugTools/DebugTools.h @@ -25,6 +25,11 @@ namespace Magnum { namespace DebugTools { /** @todoc Remove `ifndef` when Doxygen is sane again */ #ifndef DOXYGEN_GENERATING_OUTPUT +template class ForceRenderer; +typedef ForceRenderer<2> ForceRenderer2D; +typedef ForceRenderer<3> ForceRenderer3D; +class ForceRendererOptions; + template class ObjectRenderer; typedef ObjectRenderer<2> ObjectRenderer2D; typedef ObjectRenderer<3> ObjectRenderer3D; diff --git a/src/DebugTools/ForceRenderer.cpp b/src/DebugTools/ForceRenderer.cpp new file mode 100644 index 000000000..8ac198796 --- /dev/null +++ b/src/DebugTools/ForceRenderer.cpp @@ -0,0 +1,89 @@ +/* + 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. +*/ + +#include "ForceRenderer.h" + +#include "Buffer.h" +#include "Mesh.h" +#include "DebugTools/ResourceManager.h" +#include "SceneGraph/AbstractCamera.h" +#include "Shaders/FlatShader.h" + +#include "DebugTools/Implementation/ForceRendererTransformation.h" + +namespace Magnum { namespace DebugTools { + +namespace { + +template ResourceKey shaderKey(); +template<> inline ResourceKey shaderKey<2>() { return ResourceKey("FlatShader2D"); } +template<> inline ResourceKey shaderKey<3>() { return ResourceKey("FlatShader3D"); } + +constexpr std::array positions{{ + {0.0f, 0.0f}, + {1.0f, 0.0f}, + {0.9f, 0.1f}, + {0.9f, -0.1f} +}}; + +constexpr std::array indices{{ + 0, 1, + 1, 2, + 1, 3 +}}; + +} + +template ForceRenderer::ForceRenderer(SceneGraph::AbstractObject* object, const typename DimensionTraits::VectorType& forcePosition, const typename DimensionTraits::VectorType* force, ResourceKey options, SceneGraph::DrawableGroup* drawables): SceneGraph::Drawable(object, drawables), forcePosition(forcePosition), force(force), options(ResourceManager::instance()->get(options)) { + /* Shader */ + shader = ResourceManager::instance()->get>(shaderKey()); + if(!shader) ResourceManager::instance()->set(shader.key(), new Shaders::FlatShader); + + /* Mesh and vertex buffer */ + mesh = ResourceManager::instance()->get("force"); + vertexBuffer = ResourceManager::instance()->get("force-vertices"); + indexBuffer = ResourceManager::instance()->get("force-indices"); + if(mesh) return; + + /* Create the mesh */ + Buffer* vertexBuffer = new Buffer(Buffer::Target::Array); + Buffer* indexBuffer = new Buffer(Buffer::Target::ElementArray); + + vertexBuffer->setData(positions, Buffer::Usage::StaticDraw); + ResourceManager::instance()->set(this->vertexBuffer.key(), vertexBuffer, ResourceDataState::Final, ResourcePolicy::Manual); + + indexBuffer->setData(indices, Buffer::Usage::StaticDraw); + ResourceManager::instance()->set(this->indexBuffer.key(), indexBuffer, ResourceDataState::Final, ResourcePolicy::Manual); + + Mesh* mesh = new Mesh; + mesh->setPrimitive(Mesh::Primitive::Lines) + ->setIndexCount(indices.size()) + ->addVertexBuffer(vertexBuffer, 0, + typename Shaders::FlatShader::Position(Shaders::FlatShader::Position::Components::Two)) + ->setIndexBuffer(indexBuffer, 0, Mesh::IndexType::UnsignedByte, 0, positions.size()); + ResourceManager::instance()->set(this->mesh.key(), mesh, ResourceDataState::Final, ResourcePolicy::Manual); +} + +template void ForceRenderer::draw(const typename DimensionTraits::MatrixType& transformationMatrix, SceneGraph::AbstractCamera* camera) { + shader->setTransformationProjectionMatrix(camera->projectionMatrix()*Implementation::forceRendererTransformation(transformationMatrix.translation()+forcePosition, *force)*DimensionTraits::MatrixType::scaling(typename DimensionTraits::VectorType(options->scale()))) + ->setColor(options->color()) + ->use(); + mesh->draw(); +} + +template class ForceRenderer<2>; +template class ForceRenderer<3>; + +}} diff --git a/src/DebugTools/ForceRenderer.h b/src/DebugTools/ForceRenderer.h new file mode 100644 index 000000000..56af0c141 --- /dev/null +++ b/src/DebugTools/ForceRenderer.h @@ -0,0 +1,136 @@ +#ifndef Magnum_DebugTools_ForceRenderer_h +#define Magnum_DebugTools_ForceRenderer_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::DebugTools::ForceRenderer, Magnum::DebugTools::ForceRendererOptions, typedef Magnum::DebugTools::ForceRenderer2D, Magnum::DebugTools::ForceRenderer3D + */ + +#include "Color.h" +#include "Resource.h" +#include "SceneGraph/Drawable.h" +#include "Shaders/Shaders.h" + +#include "magnumDebugToolsVisibility.h" + +namespace Magnum { namespace DebugTools { + +/** +@brief Force renderer options + +See ForceRenderer documentation for more information. +*/ +class ForceRendererOptions { + public: + inline constexpr ForceRendererOptions(): _size(1.0f) {} + + /** @brief Color of rendered arrow */ + inline constexpr Color3<> color() const { return _color; } + + /** + * @brief Set color of rendered arrow + * @return Pointer to self (for method chaining) + * + * Default is black. + */ + inline ForceRendererOptions* setColor(const Color3<>& color) { + _color = color; + return this; + } + + /** @brief Scale of rendered arrow */ + inline constexpr Float scale() const { return _size; } + + /** + * @brief Set scale of rendered arrow + * @return Pointer to self (for method chaining) + * + * Default is `1.0f`. + */ + inline ForceRendererOptions* setSize(Float size) { + _size = size; + return this; + } + + private: + Color3<> _color; + Float _size; +}; + +/** +@brief Force renderer + +Visualizes force pushing on object by an arrow of the same direction and size. +See @ref debug-tools-renderers for more information. + +@section ForceRenderer-usage Basic usage + +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))); + +// Create debug renderer for given object, use "my" options for it +Object3D* object; +Vector3 force; +new DebugTools::ForceRenderer2D(object, {0.3f, 1.5f, -0.7f}, &force, "my", debugDrawables); +@endcode + +@see ForceRenderer2D, ForceRenderer3D +*/ +template class MAGNUM_DEBUGTOOLS_EXPORT ForceRenderer: public SceneGraph::Drawable { + public: + /** + * @brief Constructor + * @param object Object for which to create debug renderer + * @param forcePosition Where to render the force, relative to object + * @param force Force vector + * @param options Options resource key. See + * @ref ForceRenderer-usage "class documentation" for more + * information. + * @param drawables Drawable group + * + * The renderer is automatically added to object's features, @p force is + * saved as reference to original vector and thus it must be available + * for the whole lifetime of the renderer. + */ + explicit ForceRenderer(SceneGraph::AbstractObject* object, const typename DimensionTraits::VectorType& forcePosition, const typename DimensionTraits::VectorType* force, ResourceKey options = ResourceKey(), SceneGraph::DrawableGroup* drawables = nullptr); + + protected: + /** @todoc Remove Float when Doxygen properly treats this as override */ + void draw(const typename DimensionTraits::MatrixType& transformationMatrix, SceneGraph::AbstractCamera* camera) override; + + private: + const typename DimensionTraits::VectorType forcePosition; + const typename DimensionTraits::VectorType* const force; + + Resource options; + Resource> shader; + Resource mesh; + Resource vertexBuffer, indexBuffer; +}; + +/** @brief Two-dimensional force renderer */ +typedef ForceRenderer<2> ForceRenderer2D; + +/** @brief Three-dimensional force renderer */ +typedef ForceRenderer<3> ForceRenderer3D; + +}} + +#endif diff --git a/src/DebugTools/Implementation/ForceRendererTransformation.h b/src/DebugTools/Implementation/ForceRendererTransformation.h new file mode 100644 index 000000000..432ca276a --- /dev/null +++ b/src/DebugTools/Implementation/ForceRendererTransformation.h @@ -0,0 +1,61 @@ +#ifndef Magnum_DebugTools_Implementation_ForceRendererTransformation_h +#define Magnum_DebugTools_Implementation_ForceRendererTransformation_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. +*/ + +#include "Math/Matrix3.h" +#include "Math/Matrix4.h" +#include "Magnum.h" +#include "DimensionTraits.h" + +namespace Magnum { namespace DebugTools { namespace Implementation { + +template typename DimensionTraits::MatrixType forceRendererTransformation(const typename DimensionTraits::VectorType& forcePosition, const typename DimensionTraits::VectorType& force); + +template<> inline Matrix3 forceRendererTransformation<2>(const Vector2& forcePosition, const Vector2& force) { + return Matrix3::from({force, Vector2(-force.y(), force.x())}, forcePosition); +} + +template<> inline Matrix4 forceRendererTransformation<3>(const Vector3& forcePosition, const Vector3& force) { + const Matrix4 translation = Matrix4::translation(forcePosition); + const Float forceLength = force.length(); + + /* Zero length, zero scaling */ + if(forceLength < Math::MathTypeTraits::epsilon()) + return translation*Matrix4::scaling(Vector3(0.0f)); + + const Float dot = Vector3::dot(force/forceLength, Vector3::xAxis()); + + /* Force is parallel to X axis, just scaling */ + if(dot > 1.0f - Math::MathTypeTraits::epsilon()) + return translation*Matrix4::scaling(Vector3(forceLength)); + + /* Force is antiparallel to X axis, scaling inverted on X */ + if(-dot > 1.0f - Math::MathTypeTraits::epsilon()) + return translation*Matrix4::scaling({-forceLength, forceLength, forceLength}); + + /* Normal of plane going through force vector and X axis vector */ + const Vector3 normal = Vector3::cross(Vector3::xAxis(), force).normalized(); + + /* Third base vector, orthogonal to force and normal */ + const Vector3 binormal = Vector3::cross(normal, force).normalized(); + + /* Transformation matrix from scaled base vectors and translation vector */ + return Matrix4::from({force, normal*forceLength, binormal*forceLength}, forcePosition); +} + +}}} + +#endif diff --git a/src/DebugTools/ResourceManager.cpp b/src/DebugTools/ResourceManager.cpp index fccdd75b3..90123e984 100644 --- a/src/DebugTools/ResourceManager.cpp +++ b/src/DebugTools/ResourceManager.cpp @@ -19,16 +19,18 @@ #include "Buffer.h" #include "Mesh.h" +#include "DebugTools/ForceRenderer.h" #include "DebugTools/ObjectRenderer.h" #include "DebugTools/ShapeRenderer.h" namespace Magnum { -template class ResourceManager; +template class ResourceManager; namespace DebugTools { ResourceManager::ResourceManager() { + setFallback(new ForceRendererOptions); setFallback(new ObjectRendererOptions); setFallback(new ShapeRendererOptions); } diff --git a/src/DebugTools/ResourceManager.h b/src/DebugTools/ResourceManager.h index 3e90d8526..43429e954 100644 --- a/src/DebugTools/ResourceManager.h +++ b/src/DebugTools/ResourceManager.h @@ -34,16 +34,17 @@ namespace Magnum { -extern template ResourceManager MAGNUM_DEBUGTOOLS_EXPORT *& ResourceManager::internalInstance(); +extern template ResourceManager MAGNUM_DEBUGTOOLS_EXPORT *& ResourceManager::internalInstance(); namespace DebugTools { /** @brief %Resource manager for debug tools -Stores various data used by debug renderers. +Stores various data used by debug renderers. See @ref debug-tools for more +information. */ -class MAGNUM_DEBUGTOOLS_EXPORT ResourceManager: public Magnum::ResourceManager { +class MAGNUM_DEBUGTOOLS_EXPORT ResourceManager: public Magnum::ResourceManager { public: explicit ResourceManager(); ~ResourceManager(); diff --git a/src/DebugTools/Test/CMakeLists.txt b/src/DebugTools/Test/CMakeLists.txt new file mode 100644 index 000000000..76dcebb5e --- /dev/null +++ b/src/DebugTools/Test/CMakeLists.txt @@ -0,0 +1 @@ +corrade_add_test(MagnumDebugToolsForceRendererTest ForceRendererTest.cpp LIBRARIES MagnumMathTestLib) diff --git a/src/DebugTools/Test/ForceRendererTest.cpp b/src/DebugTools/Test/ForceRendererTest.cpp new file mode 100644 index 000000000..89b8f4131 --- /dev/null +++ b/src/DebugTools/Test/ForceRendererTest.cpp @@ -0,0 +1,114 @@ +/* + 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. +*/ + +#include + +#include "DebugTools/Implementation/ForceRendererTransformation.h" + +namespace Magnum { namespace DebugTools { namespace Implementation { namespace Test { + +class ForceRendererTest: public Corrade::TestSuite::Tester { + public: + explicit ForceRendererTest(); + + void zero2D(); + void parallel2D(); + void antiParallel2D(); + void arbitrary2D(); + + void zero3D(); + void parallel3D(); + void antiParallel3D(); + void arbitrary3D(); +}; + +ForceRendererTest::ForceRendererTest() { + addTests({&ForceRendererTest::zero2D, + &ForceRendererTest::parallel2D, + &ForceRendererTest::antiParallel2D, + &ForceRendererTest::arbitrary2D, + + &ForceRendererTest::zero3D, + &ForceRendererTest::parallel3D, + &ForceRendererTest::antiParallel3D, + &ForceRendererTest::arbitrary3D}); +} + +void ForceRendererTest::zero2D() { + CORRADE_COMPARE(Implementation::forceRendererTransformation<2>({0.5f, -3.0f}, Vector2()), + Matrix3::translation({0.5f, -3.0f})*Matrix3::scaling(Vector2(0.0f))); +} + +void ForceRendererTest::parallel2D() { + CORRADE_COMPARE(Implementation::forceRendererTransformation<2>({0.5f, -3.0f}, Vector2::xAxis(2.5f)), + Matrix3::translation({0.5f, -3.0f})*Matrix3::scaling(Vector2(2.5f))); +} + +void ForceRendererTest::antiParallel2D() { + CORRADE_COMPARE(Implementation::forceRendererTransformation<2>({0.5f, -3.0f}, Vector2::xAxis(-2.5f)), + Matrix3::translation({0.5f, -3.0f})*Matrix3::scaling(Vector2(-2.5f))); +} + +void ForceRendererTest::arbitrary2D() { + Vector2 force(2.7f, -11.5f); + Matrix3 m = Implementation::forceRendererTransformation<2>({0.5f, -3.0f}, force); + + /* Translation, right-pointing base vector is the same as force */ + CORRADE_COMPARE(m.translation(), Vector2(0.5f, -3.0f)); + CORRADE_COMPARE(m.right(), force); + + /* All vectors have the same length */ + CORRADE_COMPARE(m.up().length(), force.length()); + + /* All vectors are parallel */ + CORRADE_COMPARE(Vector2::dot(m.right(), m.up()), 0.0f); +} + +void ForceRendererTest::zero3D() { + CORRADE_COMPARE(Implementation::forceRendererTransformation<3>({0.5f, -3.0f, 1.0f}, Vector3()), + Matrix4::translation({0.5f, -3.0f, 1.0f})*Matrix4::scaling(Vector3(0.0f))); +} + +void ForceRendererTest::parallel3D() { + CORRADE_COMPARE(Implementation::forceRendererTransformation<3>({0.5f, -3.0f, 1.0f}, Vector3::xAxis(2.5f)), + Matrix4::translation({0.5f, -3.0f, 1.0f})*Matrix4::scaling(Vector3(2.5f))); +} + +void ForceRendererTest::antiParallel3D() { + CORRADE_COMPARE(Implementation::forceRendererTransformation<3>({0.5f, -3.0f, 1.0f}, Vector3::xAxis(-2.5f)), + Matrix4::translation({0.5f, -3.0f, 1.0f})*Matrix4::scaling({-2.5f, 2.5f, 2.5f})); +} + +void ForceRendererTest::arbitrary3D() { + Vector3 force(3.7f, -5.7f, -11.5f); + Matrix4 m = Implementation::forceRendererTransformation<3>({0.5f, -3.0f, 1.0f}, force); + + /* Translation, right-pointing base vector is the same as force */ + CORRADE_COMPARE(m.translation(), Vector3(0.5f, -3.0f, 1.0f)); + CORRADE_COMPARE(m.right(), force); + + /* All vectors have the same length */ + CORRADE_COMPARE(m.up().length(), force.length()); + CORRADE_COMPARE(m.backward().length(), force.length()); + + /* All vectors are parallel */ + CORRADE_COMPARE(Vector3::dot(m.right(), m.up()), 0.0f); + CORRADE_COMPARE(Vector3::dot(m.right(), m.backward()), 0.0f); + CORRADE_COMPARE(Vector3::dot(m.up(), m.backward()), 0.0f); +} + +}}}} + +CORRADE_TEST_MAIN(Magnum::DebugTools::Implementation::Test::ForceRendererTest)