diff --git a/src/DebugTools/CMakeLists.txt b/src/DebugTools/CMakeLists.txt index 00d88ecc8..d348e3b8b 100644 --- a/src/DebugTools/CMakeLists.txt +++ b/src/DebugTools/CMakeLists.txt @@ -34,6 +34,7 @@ set(MagnumDebugTools_SRCS Implementation/AxisAlignedBoxRenderer.cpp Implementation/BoxRenderer.cpp Implementation/CapsuleRenderer.cpp + Implementation/CylinderRenderer.cpp Implementation/LineSegmentRenderer.cpp Implementation/PointRenderer.cpp Implementation/SphereRenderer.cpp) diff --git a/src/DebugTools/Implementation/CylinderRenderer.cpp b/src/DebugTools/Implementation/CylinderRenderer.cpp new file mode 100644 index 000000000..f7cfbd1e2 --- /dev/null +++ b/src/DebugTools/Implementation/CylinderRenderer.cpp @@ -0,0 +1,61 @@ +/* + 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 "CylinderRenderer.h" + +#include "Mesh.h" +#include "DebugTools/ShapeRenderer.h" +#include "Shapes/Cylinder.h" +#include "Primitives/Cylinder.h" +#include "Primitives/Square.h" +#include "Shaders/Flat.h" +#include "Trade/MeshData2D.h" +#include "Trade/MeshData3D.h" + +#include "DebugTools/Implementation/CylinderRendererTransformation.h" + +namespace Magnum { namespace DebugTools { namespace Implementation { + +AbstractCylinderRenderer<2>::AbstractCylinderRenderer(): AbstractShapeRenderer<2>("cylinder2d", "cylinder2d-vertices", {}) { + if(!wireframeMesh) createResources(Primitives::Square::wireframe()); +} + +AbstractCylinderRenderer<3>::AbstractCylinderRenderer(): AbstractShapeRenderer<3>("cylinder3d", "cylinder3d-vertices", "cylinder3d-indices") { + if(!wireframeMesh) createResources(Primitives::Cylinder::wireframe(1, 40, 1.0f)); +} + +template CylinderRenderer::CylinderRenderer(const Shapes::Implementation::AbstractShape& cylinder): cylinder(static_cast>&>(cylinder).shape) {} + +template void CylinderRenderer::draw(Resource& options, const typename DimensionTraits::MatrixType& projectionMatrix) { + AbstractShapeRenderer::wireframeShader->setTransformationProjectionMatrix(projectionMatrix* + Implementation::cylinderRendererTransformation(cylinder.a(), cylinder.b(), cylinder.radius())) + .setColor(options->color()) + .use(); + AbstractShapeRenderer::wireframeMesh->draw(); +} + +template class CylinderRenderer<2>; +template class CylinderRenderer<3>; + +}}} diff --git a/src/DebugTools/Implementation/CylinderRenderer.h b/src/DebugTools/Implementation/CylinderRenderer.h new file mode 100644 index 000000000..d5a511254 --- /dev/null +++ b/src/DebugTools/Implementation/CylinderRenderer.h @@ -0,0 +1,60 @@ +#ifndef Magnum_DebugTools_Implementation_CylinderRenderer_h +#define Magnum_DebugTools_Implementation_CylinderRenderer_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 "Shapes/Shapes.h" + +#include "corradeCompatibility.h" + +namespace Magnum { namespace DebugTools { namespace Implementation { + +template class AbstractCylinderRenderer; + +template<> class AbstractCylinderRenderer<2>: public AbstractShapeRenderer<2> { + public: + explicit AbstractCylinderRenderer(); +}; + +template<> class AbstractCylinderRenderer<3>: public AbstractShapeRenderer<3> { + public: + explicit AbstractCylinderRenderer(); +}; + +template class CylinderRenderer: public AbstractCylinderRenderer { + public: + explicit CylinderRenderer(const Shapes::Implementation::AbstractShape& cylinder); + CylinderRenderer(const Shapes::Implementation::AbstractShape&&) = delete; + + void draw(Resource& options, const typename DimensionTraits::MatrixType& projectionMatrix) override; + + private: + const Shapes::Cylinder& cylinder; +}; + +}}} + +#endif diff --git a/src/DebugTools/Implementation/CylinderRendererTransformation.h b/src/DebugTools/Implementation/CylinderRendererTransformation.h new file mode 100644 index 000000000..4caf8c5d6 --- /dev/null +++ b/src/DebugTools/Implementation/CylinderRendererTransformation.h @@ -0,0 +1,88 @@ +#ifndef Magnum_DebugTools_Implementation_ForceRendererTransformation_h +#define Magnum_DebugTools_Implementation_ForceRendererTransformation_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 "Math/Functions.h" +#include "Math/Matrix3.h" +#include "Math/Matrix4.h" +#include "Magnum.h" +#include "DimensionTraits.h" + +namespace Magnum { namespace DebugTools { namespace Implementation { + +template typename DimensionTraits::MatrixType cylinderRendererTransformation(const typename DimensionTraits::VectorType& a, const typename DimensionTraits::VectorType& b, Float radius); + +template<> Matrix3 cylinderRendererTransformation<2>(const Vector2& a, const Vector2& b, const Float radius) { + /* Vector from cylinder center to top hemisphere center */ + const Vector2 direction = 0.5f*(b - a); + const Float length = direction.length(); + + /* Capsule rotation and distance to caps after they are scaled to proper + radius (if nonzero cylinder length) */ + Matrix3 rotation; + if(length >= Math::TypeTraits::epsilon()) { + rotation.up() = direction/length; + rotation.right() = rotation.up().perpendicular(); + CORRADE_INTERNAL_ASSERT(rotation.right().isNormalized()); + } + + /* Scaling and translation */ + return Matrix3::translation(0.5f*(a + b))*rotation*Matrix3::scaling({radius, length}); +} + +template<> Matrix4 cylinderRendererTransformation<3>(const Vector3& a, const Vector3& b, const Float radius) { + /* Vector from cylinder center to top hemisphere center */ + const Vector3 direction = 0.5f*(b - a); + const Float length = direction.length(); + + /* Capsule rotation and distance to caps after they are scaled to proper + radius (if nonzero cylinder length) */ + Matrix4 rotation; + if(length >= Math::TypeTraits::epsilon()) { + const Vector3 directionNormalized = direction/length; + const Float dot = Vector3::dot(directionNormalized, Vector3::zAxis()); + + /* Direction is parallel to Z axis, special rotation case */ + if(Math::abs(dot) > 1.0f - Math::TypeTraits::epsilon()) { + rotation.up() = dot*Vector3::zAxis(); + rotation.right() = Vector3::xAxis(); + rotation.backward() = -dot*Vector3::yAxis(); + + /* Common case */ + } else { + rotation.up() = directionNormalized; + rotation.right() = Vector3::cross(rotation.up(), Vector3::zAxis()).normalized(); + rotation.backward() = Vector3::cross(rotation.right(), rotation.up()); + CORRADE_INTERNAL_ASSERT(rotation.up().isNormalized() && rotation.backward().isNormalized()); + } + } + + /* Scaling and translation */ + return Matrix4::translation(0.5f*(a + b))*rotation*Matrix4::scaling({radius, length, radius}); +} + +}}} + +#endif diff --git a/src/DebugTools/ShapeRenderer.cpp b/src/DebugTools/ShapeRenderer.cpp index 62cbc8f37..28118071c 100644 --- a/src/DebugTools/ShapeRenderer.cpp +++ b/src/DebugTools/ShapeRenderer.cpp @@ -32,6 +32,7 @@ #include "Implementation/AxisAlignedBoxRenderer.h" #include "Implementation/BoxRenderer.h" #include "Implementation/CapsuleRenderer.h" +#include "Implementation/CylinderRenderer.h" #include "Implementation/LineSegmentRenderer.h" #include "Implementation/PointRenderer.h" #include "Implementation/SphereRenderer.h" @@ -60,6 +61,9 @@ template<> void createDebugMesh(ShapeRenderer<2>& renderer, const Shapes::Implem case Shapes::AbstractShape2D::Type::Capsule: renderer.renderers.push_back(new Implementation::CapsuleRenderer<2>(shape)); break; + case Shapes::AbstractShape2D::Type::Cylinder: + renderer.renderers.push_back(new Implementation::CylinderRenderer<2>(shape)); + break; case Shapes::AbstractShape2D::Type::Composition: { const Shapes::Composition2D& composition = static_cast&>(shape).shape; @@ -91,6 +95,9 @@ template<> void createDebugMesh(ShapeRenderer<3>& renderer, const Shapes::Implem case Shapes::AbstractShape3D::Type::Capsule: renderer.renderers.push_back(new Implementation::CapsuleRenderer<3>(shape)); break; + case Shapes::AbstractShape3D::Type::Cylinder: + renderer.renderers.push_back(new Implementation::CylinderRenderer<3>(shape)); + break; case Shapes::AbstractShape3D::Type::Composition: { const Shapes::Composition3D& composition = static_cast&>(shape).shape; diff --git a/src/DebugTools/Test/CMakeLists.txt b/src/DebugTools/Test/CMakeLists.txt index ba5e678e2..35fca1640 100644 --- a/src/DebugTools/Test/CMakeLists.txt +++ b/src/DebugTools/Test/CMakeLists.txt @@ -23,5 +23,6 @@ # corrade_add_test(DebugToolsCapsuleRendererTest CapsuleRendererTest.cpp LIBRARIES MagnumMathTestLib) +corrade_add_test(DebugToolsCylinderRendererTest CylinderRendererTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(DebugToolsForceRendererTest ForceRendererTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(DebugToolsLineSegmentRendererTest LineSegmentRendererTest.cpp LIBRARIES MagnumMathTestLib) diff --git a/src/DebugTools/Test/CylinderRendererTest.cpp b/src/DebugTools/Test/CylinderRendererTest.cpp new file mode 100644 index 000000000..958003f96 --- /dev/null +++ b/src/DebugTools/Test/CylinderRendererTest.cpp @@ -0,0 +1,125 @@ +/* + 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 "DebugTools/Implementation/CylinderRendererTransformation.h" + +namespace Magnum { namespace DebugTools { namespace Test { + +class CylinderRendererTest: public TestSuite::Tester { + public: + explicit CylinderRendererTest(); + + void zeroLength2D(); + void common2D(); + + void zeroLength3D(); + void parallel3D(); + void antiParallel3D(); + void common3D(); +}; + +CylinderRendererTest::CylinderRendererTest() { + addTests({&CylinderRendererTest::zeroLength2D, + &CylinderRendererTest::common2D, + + &CylinderRendererTest::zeroLength3D, + &CylinderRendererTest::parallel3D, + &CylinderRendererTest::antiParallel3D, + &CylinderRendererTest::common3D}); +} + +void CylinderRendererTest::zeroLength2D() { + const Vector2 a(0.5f, 3.0f); + const Matrix3 transformation = Implementation::cylinderRendererTransformation<2>(a, a, 3.5f); + + CORRADE_COMPARE(transformation.rotationScaling(), (Math::Matrix<2, Float>::fromDiagonal({3.5f, 0.0f}))); + CORRADE_COMPARE(transformation.translation(), a); +} + +void CylinderRendererTest::common2D() { + const Vector2 a(0.5f, 3.0f); + const Vector2 b(7.5f, -1.0f); + const Matrix3 transformation = Implementation::cylinderRendererTransformation<2>(a, b, 3.5f); + + /* Rotation + scaling, test orthogonality */ + CORRADE_COMPARE(transformation.up(), Vector2(3.5f, -2.0f)); + CORRADE_COMPARE(transformation.right(), Vector2(4.0f, 7.0f).resized(3.5f)); + CORRADE_COMPARE(Vector2::dot(transformation.up(), transformation.right()), 0.0f); + + CORRADE_COMPARE(transformation.translation(), 0.5f*(a + b)); +} + +void CylinderRendererTest::zeroLength3D() { + const Vector3 a(0.5f, 3.0f, 7.0f); + const Matrix4 transformation = Implementation::cylinderRendererTransformation<3>(a, a, 3.5f); + + CORRADE_COMPARE(transformation.rotationScaling(), (Math::Matrix<3, Float>::fromDiagonal({3.5f, 0.0f, 3.5f}))); + CORRADE_COMPARE(transformation.translation(), a); +} + +void CylinderRendererTest::parallel3D() { + const Vector3 a(0.5f, 3.0f, 7.0f); + const Vector3 b(0.5f, 3.0f, 11.0f); + const Matrix4 transformation = Implementation::cylinderRendererTransformation<3>(a, b, 3.5f); + + CORRADE_COMPARE(transformation.rotationScaling(), + (Matrix4::rotationX(Deg(90.0f))*Matrix4::scaling({3.5f, 2.0f, 3.5f})).rotationScaling()); + + CORRADE_COMPARE(transformation.translation(), a+Vector3::zAxis(2.0f)); +} + +void CylinderRendererTest::antiParallel3D() { + const Vector3 a(0.5f, 3.0f, 7.0f); + const Vector3 b(0.5f, 3.0f, 3.0f); + const Matrix4 transformation = Implementation::cylinderRendererTransformation<3>(a, b, 3.5f); + + CORRADE_COMPARE(transformation.rotationScaling(), + (Matrix4::rotationX(-Deg(90.0f))*Matrix4::scaling({3.5f, 2.0f, 3.5f})).rotationScaling()); + + CORRADE_COMPARE(transformation.translation(), a+Vector3::zAxis(-2.0f)); +} + +void CylinderRendererTest::common3D() { + const Vector3 a(0.5f, 3.0f, 7.0f); + const Vector3 b(7.5f, -1.0f, 1.5f); + const Matrix4 transformation = Implementation::cylinderRendererTransformation<3>(a, b, 3.5f); + + /* Rotation + scaling */ + CORRADE_COMPARE(transformation.up(), Vector3(3.5f, -2.0f, -2.75f)); + CORRADE_COMPARE(transformation.right(), Vector3(-2.0f, -3.5f, 0.0f).resized(3.5f)); + CORRADE_COMPARE(transformation.backward(), Vector3(9.625f, -5.5f, 16.25f).resized(3.5f)); + + /* Orthogonality */ + CORRADE_COMPARE(Vector3::dot(transformation.up(), transformation.right()), 0.0f); + CORRADE_COMPARE(Vector3::dot(transformation.up(), transformation.backward()), 0.0f); + CORRADE_COMPARE(Vector3::dot(transformation.right(), transformation.backward()), 0.0f); + + CORRADE_COMPARE(transformation.translation(), 0.5f*(a + b)); +} + +}}} + +CORRADE_TEST_MAIN(Magnum::DebugTools::Test::CylinderRendererTest)