From d13388f7bccd760a486d71eb7988bb814397f57a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 22 Jul 2013 23:35:26 +0200 Subject: [PATCH] Primitives: wireframe versions of Capsule, Cylinder and UVSphere. Simplified representation of the shapes, i.e. an UVSphere is visualized by three circles, one for each axis. Will be used mainly in DebugTools. --- src/Primitives/CMakeLists.txt | 3 +- src/Primitives/Capsule.cpp | 22 ++++ src/Primitives/Capsule.h | 14 +++ src/Primitives/Cylinder.cpp | 16 +++ src/Primitives/Cylinder.h | 12 ++ .../Implementation/WireframeSpheroid.cpp | 115 ++++++++++++++++++ .../Implementation/WireframeSpheroid.h | 54 ++++++++ src/Primitives/Test/CapsuleTest.cpp | 82 ++++++++++++- src/Primitives/Test/CylinderTest.cpp | 64 +++++++++- src/Primitives/Test/UVSphereTest.cpp | 56 ++++++++- src/Primitives/UVSphere.cpp | 14 +++ src/Primitives/UVSphere.h | 11 ++ 12 files changed, 444 insertions(+), 19 deletions(-) create mode 100644 src/Primitives/Implementation/WireframeSpheroid.cpp create mode 100644 src/Primitives/Implementation/WireframeSpheroid.h diff --git a/src/Primitives/CMakeLists.txt b/src/Primitives/CMakeLists.txt index 01224000d..8ee9e9951 100644 --- a/src/Primitives/CMakeLists.txt +++ b/src/Primitives/CMakeLists.txt @@ -34,7 +34,8 @@ set(MagnumPrimitives_SRCS Square.cpp UVSphere.cpp - Implementation/Spheroid.cpp) + Implementation/Spheroid.cpp + Implementation/WireframeSpheroid.cpp) set(MagnumPrimitives_HEADERS Capsule.h diff --git a/src/Primitives/Capsule.cpp b/src/Primitives/Capsule.cpp index d0128ba53..50885b3bd 100644 --- a/src/Primitives/Capsule.cpp +++ b/src/Primitives/Capsule.cpp @@ -26,6 +26,7 @@ #include "Math/Vector3.h" #include "Primitives/Implementation/Spheroid.h" +#include "Primitives/Implementation/WireframeSpheroid.h" #include "Trade/MeshData3D.h" namespace Magnum { namespace Primitives { @@ -64,4 +65,25 @@ Trade::MeshData3D Capsule::solid(UnsignedInt hemisphereRings, UnsignedInt cylind return capsule.finalize(); } +Trade::MeshData3D Capsule::wireframe(const UnsignedInt hemisphereRings, const UnsignedInt cylinderRings, const UnsignedInt segments, const Float length) { + CORRADE_ASSERT(hemisphereRings >= 1 && cylinderRings >= 1 && segments >= 4 && segments%4 == 0, "Primitives::Capsule::wireframe(): improper parameters", Trade::MeshData3D(Mesh::Primitive::Lines, {}, {}, {}, {})); + + Implementation::WireframeSpheroid capsule(segments/4); + + /* Bottom hemisphere */ + capsule.bottomHemisphere(-length/2, hemisphereRings); + + /* Cylinder */ + capsule.ring(-length/2); + for(UnsignedInt i = 0; i != cylinderRings; ++i) { + capsule.cylinder(); + capsule.ring(-length/2 + (i+1)*(length/cylinderRings)); + } + + /* Top hemisphere */ + capsule.topHemisphere(length/2, hemisphereRings); + + return capsule.finalize(); +} + }} diff --git a/src/Primitives/Capsule.h b/src/Primitives/Capsule.h index fbefe1c30..bf9195aed 100644 --- a/src/Primitives/Capsule.h +++ b/src/Primitives/Capsule.h @@ -62,6 +62,20 @@ class MAGNUM_PRIMITIVES_EXPORT Capsule { * vertices of one segment are duplicated for texture wrapping. */ static Trade::MeshData3D solid(UnsignedInt hemisphereRings, UnsignedInt cylinderRings, UnsignedInt segments, Float length, TextureCoords textureCoords = TextureCoords::DontGenerate); + + /** + * @brief Wireframe capsule + * @param hemisphereRings Number of (line) rings for each hemisphere. + * Must be larger or equal to 1. + * @param cylinderRings Number of (line) rings for cylinder. Must be + * larger or equal to 1. + * @param segments Number of line segments. Must be larger or + * equal to 4 and multiple of 4. + * @param length Length of the capsule, excluding hemispheres. + * + * Indexed @ref Mesh::Primitive "Lines". + */ + static Trade::MeshData3D wireframe(UnsignedInt hemisphereRings, UnsignedInt cylinderRings, UnsignedInt segments, Float length); }; }} diff --git a/src/Primitives/Cylinder.cpp b/src/Primitives/Cylinder.cpp index a750715ae..b1af5e6a8 100644 --- a/src/Primitives/Cylinder.cpp +++ b/src/Primitives/Cylinder.cpp @@ -26,6 +26,7 @@ #include "Math/Vector3.h" #include "Primitives/Implementation/Spheroid.h" +#include "Primitives/Implementation/WireframeSpheroid.h" #include "Trade/MeshData3D.h" namespace Magnum { namespace Primitives { @@ -61,4 +62,19 @@ Trade::MeshData3D Cylinder::solid(UnsignedInt rings, UnsignedInt segments, Float return cylinder.finalize(); } +Trade::MeshData3D Cylinder::wireframe(const UnsignedInt rings, const UnsignedInt segments, const Float length) { + CORRADE_ASSERT(rings >= 1 && segments >= 4 && segments%4 == 0, "Primitives::Cylinder::wireframe(): improper parameters", Trade::MeshData3D(Mesh::Primitive::Lines, {}, {}, {}, {})); + + Implementation::WireframeSpheroid cylinder(segments/4); + + /* Rings */ + cylinder.ring(-length/2); + for(UnsignedInt i = 0; i != rings; ++i) { + cylinder.cylinder(); + cylinder.ring(-length/2 + (i+1)*(length/rings)); + } + + return cylinder.finalize(); +} + }} diff --git a/src/Primitives/Cylinder.h b/src/Primitives/Cylinder.h index 0a08fcf2b..8b8d92bfb 100644 --- a/src/Primitives/Cylinder.h +++ b/src/Primitives/Cylinder.h @@ -71,6 +71,18 @@ class MAGNUM_PRIMITIVES_EXPORT Cylinder { * wrapping. */ static Trade::MeshData3D solid(UnsignedInt rings, UnsignedInt segments, Float length, Flags flags = Flags()); + + /** + * @brief Wireframe cylinder + * @param rings Number of (line) rings. Must be larger or equal + * to 1. + * @param segments Number of (line) segments. Must be larger or + * equal to 4 and multiple of 4. + * @param length Cylinder length + * + * Indexed @ref Mesh::Primitive "Lines". + */ + static Trade::MeshData3D wireframe(UnsignedInt rings, UnsignedInt segments, Float length); }; CORRADE_ENUMSET_OPERATORS(Cylinder::Flags) diff --git a/src/Primitives/Implementation/WireframeSpheroid.cpp b/src/Primitives/Implementation/WireframeSpheroid.cpp new file mode 100644 index 000000000..67bbaeed4 --- /dev/null +++ b/src/Primitives/Implementation/WireframeSpheroid.cpp @@ -0,0 +1,115 @@ +/* + 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 "WireframeSpheroid.h" + +#include "Math/Functions.h" +#include "Math/Vector3.h" +#include "Trade/MeshData3D.h" + +namespace Magnum { namespace Primitives { namespace Implementation { + +WireframeSpheroid::WireframeSpheroid(const UnsignedInt segments): _segments(segments) {} + +void WireframeSpheroid::bottomHemisphere(const Float endY, const UnsignedInt rings) { + CORRADE_INTERNAL_ASSERT(_positions.empty()); + + /* Initial vertex */ + _positions.push_back(Vector3::yAxis(endY - 1.0f)); + + /* Connect initial vertex to first ring */ + for(UnsignedInt i = 0; i != 4; ++i) + _indices.insert(_indices.end(), {0, i+1}); + + /* Hemisphere vertices and indices */ + const Rad ringAngleIncrement(Constants::pi()/(2*rings)); + for(UnsignedInt j = 0; j != rings-1; ++j) { + const Rad angle = (j+1)*ringAngleIncrement; + + _positions.emplace_back(0.0f, endY - Math::cos(angle), Math::sin(angle)); + _positions.emplace_back(Math::sin(angle), endY - Math::cos(angle), 0.0f); + _positions.emplace_back(0.0f, endY - Math::cos(angle), -Math::sin(angle)); + _positions.emplace_back(-Math::sin(angle), endY - Math::cos(angle), 0.0f); + + /* Connect vertices to next ring */ + for(UnsignedInt i = 0; i != 4; ++i) + _indices.insert(_indices.end(), {UnsignedInt(_positions.size())-4+i, UnsignedInt(_positions.size())+i}); + } +} + +void WireframeSpheroid::topHemisphere(const Float startY, const UnsignedInt rings) { + /* Connect previous ring to following vertices */ + for(UnsignedInt i = 0; i != 4; ++i) + _indices.insert(_indices.end(), {UnsignedInt(_positions.size())-4*_segments+i, UnsignedInt(_positions.size())+i}); + + /* Hemisphere vertices and indices */ + const Rad ringAngleIncrement(Constants::pi()/(2*rings)); + for(UnsignedInt j = 0; j != rings-1; ++j) { + const Rad angle = (j+1)*ringAngleIncrement; + + /* Connect previous hemisphere ring to current vertices */ + if(j != 0) for(UnsignedInt i = 0; i != 4; ++i) + _indices.insert(_indices.end(), {UnsignedInt(_positions.size())-4+i, UnsignedInt(_positions.size())+i}); + + _positions.emplace_back(0.0f, startY + Math::sin(angle), Math::cos(angle)); + _positions.emplace_back(Math::cos(angle), startY + Math::sin(angle), 0.0f); + _positions.emplace_back(0.0f, startY + Math::sin(angle), -Math::cos(angle)); + _positions.emplace_back(-Math::cos(angle), startY + Math::sin(angle), 0.0f); + } + + /* Final vertex */ + _positions.push_back(Vector3::yAxis(startY + 1.0f)); + + /* Connect last ring to final vertex */ + for(UnsignedInt i = 0; i != 4; ++i) + _indices.insert(_indices.end(), {UnsignedInt(_positions.size())-5+i, UnsignedInt(_positions.size())-1}); +} + +void WireframeSpheroid::ring(const Float y) { + /* Ring vertices and indices */ + const Rad segmentAngleIncrement(Constants::pi()/(2*_segments)); + for(UnsignedInt j = 0; j != _segments; ++j) { + for(UnsignedInt i = 0; i != 4; ++i) { + const Rad segmentAngle = Rad(i*Constants::pi()/2) + j*segmentAngleIncrement; + if(j != 0) _indices.insert(_indices.end(), {UnsignedInt(_positions.size()-4), UnsignedInt(_positions.size())}); + _positions.emplace_back(Math::sin(segmentAngle), y, Math::cos(segmentAngle)); + } + } + + /* Close the ring */ + for(UnsignedInt i = 0; i != 4; ++i) + _indices.insert(_indices.end(), {UnsignedInt(_positions.size())-4+i, UnsignedInt(_positions.size())-4*_segments+(i+1)%4}); +} + +void WireframeSpheroid::cylinder() { + /* Connect four vertex pairs of previous and next ring */ + for(UnsignedInt i = 0; i != 4; ++i) + _indices.insert(_indices.end(), {UnsignedInt(_positions.size())-4*_segments+i, UnsignedInt(_positions.size())+i}); +} + +Trade::MeshData3D WireframeSpheroid::finalize() { + return Trade::MeshData3D(Mesh::Primitive::Lines, std::move(_indices), {std::move(_positions)}, {}, {}); +} + +}}} diff --git a/src/Primitives/Implementation/WireframeSpheroid.h b/src/Primitives/Implementation/WireframeSpheroid.h new file mode 100644 index 000000000..04f40fc5f --- /dev/null +++ b/src/Primitives/Implementation/WireframeSpheroid.h @@ -0,0 +1,54 @@ +#ifndef Magnum_Primitives_WireframeSpheroid_h +#define Magnum_Primitives_WireframeSpheroid_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 + +#include "Magnum.h" +#include "Trade/Trade.h" + +namespace Magnum { namespace Primitives { namespace Implementation { + +class WireframeSpheroid { + public: + WireframeSpheroid(UnsignedInt segments); + + void bottomHemisphere(Float endY, UnsignedInt rings); + void topHemisphere(Float startY, UnsignedInt rings); + void ring(Float y); + void cylinder(); + + Trade::MeshData3D finalize(); + + private: + UnsignedInt _segments; + + std::vector _indices; + std::vector _positions; +}; + +}}} + +#endif diff --git a/src/Primitives/Test/CapsuleTest.cpp b/src/Primitives/Test/CapsuleTest.cpp index 4e41eae02..77e7be8e3 100644 --- a/src/Primitives/Test/CapsuleTest.cpp +++ b/src/Primitives/Test/CapsuleTest.cpp @@ -38,16 +38,18 @@ class CapsuleTest: public TestSuite::Tester { public: CapsuleTest(); - void withoutTextureCoords(); - void withTextureCoords(); + void solidWithoutTextureCoords(); + void solidWithTextureCoords(); + void wireframe(); }; CapsuleTest::CapsuleTest() { - addTests({&CapsuleTest::withoutTextureCoords, - &CapsuleTest::withTextureCoords}); + addTests({&CapsuleTest::solidWithoutTextureCoords, + &CapsuleTest::solidWithTextureCoords, + &CapsuleTest::wireframe}); } -void CapsuleTest::withoutTextureCoords() { +void CapsuleTest::solidWithoutTextureCoords() { Trade::MeshData3D capsule = Capsule::solid(2, 2, 3, 1.0f); CORRADE_COMPARE_AS(capsule.positions(0), (std::vector{ @@ -112,7 +114,7 @@ void CapsuleTest::withoutTextureCoords() { }), TestSuite::Compare::Container); } -void CapsuleTest::withTextureCoords() { +void CapsuleTest::solidWithTextureCoords() { Trade::MeshData3D capsule = Capsule::solid(2, 2, 3, 1.0f, Capsule::TextureCoords::Generate); CORRADE_COMPARE_AS(capsule.positions(0), (std::vector{ @@ -187,6 +189,74 @@ void CapsuleTest::withTextureCoords() { }), TestSuite::Compare::Container); } +void CapsuleTest::wireframe() { + Trade::MeshData3D capsule = Capsule::wireframe(2, 2, 8, 1.0f); + + CORRADE_COMPARE_AS(capsule.positions(0), (std::vector{ + {0.0f, -1.5f, 0.0f}, + + {0.0f, -1.20711f, 0.707107f}, + {0.707107f, -1.20711f, 0.0f}, + {0.0f, -1.20711f, -0.707107f}, + {-0.707107f, -1.20711f, 0.0f}, + + {0.0f, -0.5f, 1.0f}, + {1.0f, -0.5f, 0.0f}, + {0.0f, -0.5f, -1.0f}, + {-1.0f, -0.5f, 0.0f}, + {0.707107f, -0.5f, 0.707107f}, + {0.707107f, -0.5f, -0.707107f}, + {-0.707107f, -0.5f, -0.707107f}, + {-0.707107f, -0.5f, 0.707107f}, + + {0.0f, 0.0f, 1.0f}, + {1.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, -1.0f}, + {-1.0f, 0.0f, 0.0f}, + {0.707107f, 0.0f, 0.707107f}, + {0.707107f, 0.0f, -0.707107f}, + {-0.707107f, 0.0f, -0.707107f}, + {-0.707107f, 0.0f, 0.707107f}, + + {0.0f, 0.5f, 1.0f}, + {1.0f, 0.5f, 0.0f}, + {0.0f, 0.5f, -1.0f}, + {-1.0f, 0.5f, 0.0f}, + {0.707107f, 0.5f, 0.707107f}, + {0.707107f, 0.5f, -0.707107f}, + {-0.707107f, 0.5f, -0.707107f}, + {-0.707107f, 0.5f, 0.707107f}, + + {0.0f, 1.20711f, 0.707107f}, + {0.707107f, 1.20711f, 0.0f}, + {0.0f, 1.20711f, -0.707107f}, + {-0.707107f, 1.20711f, 0.0f}, + + {0.0f, 1.5f, 0.0f} + }), TestSuite::Compare::Container); + + CORRADE_COMPARE(capsule.normalArrayCount(), 0); + + CORRADE_COMPARE_AS(capsule.indices(), (std::vector{ + 0, 1, 0, 2, 0, 3, 0, 4, + 1, 5, 2, 6, 3, 7, 4, 8, + 5, 9, 6, 10, 7, 11, 8, 12, + 9, 6, 10, 7, 11, 8, 12, 5, + + 5, 13, 6, 14, 7, 15, 8, 16, + + 13, 17, 14, 18, 15, 19, 16, 20, + 17, 14, 18, 15, 19, 16, 20, 13, + + 13, 21, 14, 22, 15, 23, 16, 24, + + 21, 25, 22, 26, 23, 27, 24, 28, + 25, 22, 26, 23, 27, 24, 28, 21, + 21, 29, 22, 30, 23, 31, 24, 32, + 29, 33, 30, 33, 31, 33, 32, 33 + }), TestSuite::Compare::Container); +} + }}} CORRADE_TEST_MAIN(Magnum::Primitives::Test::CapsuleTest) diff --git a/src/Primitives/Test/CylinderTest.cpp b/src/Primitives/Test/CylinderTest.cpp index 33e8a7d43..7aa532bf6 100644 --- a/src/Primitives/Test/CylinderTest.cpp +++ b/src/Primitives/Test/CylinderTest.cpp @@ -35,16 +35,18 @@ class CylinderTest: public TestSuite::Tester { public: CylinderTest(); - void withoutAnything(); - void withTextureCoordsAndCaps(); + void solidWithoutAnything(); + void solidWithTextureCoordsAndCaps(); + void wireframe(); }; CylinderTest::CylinderTest() { - addTests({&CylinderTest::withoutAnything, - &CylinderTest::withTextureCoordsAndCaps}); + addTests({&CylinderTest::solidWithoutAnything, + &CylinderTest::solidWithTextureCoordsAndCaps, + &CylinderTest::wireframe}); } -void CylinderTest::withoutAnything() { +void CylinderTest::solidWithoutAnything() { Trade::MeshData3D cylinder = Cylinder::solid(2, 3, 3.0f); CORRADE_COMPARE_AS(cylinder.positions(0), (std::vector{ @@ -81,7 +83,7 @@ void CylinderTest::withoutAnything() { }), TestSuite::Compare::Container); } -void CylinderTest::withTextureCoordsAndCaps() { +void CylinderTest::solidWithTextureCoordsAndCaps() { Trade::MeshData3D cylinder = Cylinder::solid(2, 3, 3.0f, Cylinder::Flag::GenerateTextureCoords|Cylinder::Flag::CapEnds); CORRADE_COMPARE_AS(cylinder.positions(0), (std::vector{ @@ -185,6 +187,56 @@ void CylinderTest::withTextureCoordsAndCaps() { }), TestSuite::Compare::Container); } +void CylinderTest::wireframe() { + Trade::MeshData3D cylinder = Cylinder::wireframe(2, 8, 1.0f); + + CORRADE_COMPARE_AS(cylinder.positions(0), (std::vector{ + {0.0f, -0.5f, 1.0f}, + {1.0f, -0.5f, 0.0f}, + {0.0f, -0.5f, -1.0f}, + {-1.0f, -0.5f, 0.0f}, + {0.707107f, -0.5f, 0.707107f}, + {0.707107f, -0.5f, -0.707107f}, + {-0.707107f, -0.5f, -0.707107f}, + {-0.707107f, -0.5f, 0.707107f}, + + {0.0f, 0.0f, 1.0f}, + {1.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, -1.0f}, + {-1.0f, 0.0f, 0.0f}, + {0.707107f, 0.0f, 0.707107f}, + {0.707107f, 0.0f, -0.707107f}, + {-0.707107f, 0.0f, -0.707107f}, + {-0.707107f, 0.0f, 0.707107f}, + + {0.0f, 0.5f, 1.0f}, + {1.0f, 0.5f, 0.0f}, + {0.0f, 0.5f, -1.0f}, + {-1.0f, 0.5f, 0.0f}, + {0.707107f, 0.5f, 0.707107f}, + {0.707107f, 0.5f, -0.707107f}, + {-0.707107f, 0.5f, -0.707107f}, + {-0.707107f, 0.5f, 0.707107f} + }), TestSuite::Compare::Container); + + CORRADE_COMPARE(cylinder.normalArrayCount(), 0); + + CORRADE_COMPARE_AS(cylinder.indices(), (std::vector{ + 0, 4, 1, 5, 2, 6, 3, 7, + 4, 1, 5, 2, 6, 3, 7, 0, + + 0, 8, 1, 9, 2, 10, 3, 11, + + 8, 12, 9, 13, 10, 14, 11, 15, + 12, 9, 13, 10, 14, 11, 15, 8, + + 8, 16, 9, 17, 10, 18, 11, 19, + + 16, 20, 17, 21, 18, 22, 19, 23, + 20, 17, 21, 18, 22, 19, 23, 16 + }), TestSuite::Compare::Container); +} + }}} CORRADE_TEST_MAIN(Magnum::Primitives::Test::CylinderTest) diff --git a/src/Primitives/Test/UVSphereTest.cpp b/src/Primitives/Test/UVSphereTest.cpp index 46f25bbf0..f4df1a54e 100644 --- a/src/Primitives/Test/UVSphereTest.cpp +++ b/src/Primitives/Test/UVSphereTest.cpp @@ -35,16 +35,18 @@ class UVSphereTest: public TestSuite::Tester { public: UVSphereTest(); - void withoutTextureCoords(); - void withTextureCoords(); + void solidWithoutTextureCoords(); + void solidWithTextureCoords(); + void wireframe(); }; UVSphereTest::UVSphereTest() { - addTests({&UVSphereTest::withoutTextureCoords, - &UVSphereTest::withTextureCoords}); + addTests({&UVSphereTest::solidWithoutTextureCoords, + &UVSphereTest::solidWithTextureCoords, + &UVSphereTest::wireframe}); } -void UVSphereTest::withoutTextureCoords() { +void UVSphereTest::solidWithoutTextureCoords() { Trade::MeshData3D sphere = UVSphere::solid(3, 3); CORRADE_COMPARE_AS(sphere.positions(0), (std::vector{ @@ -82,7 +84,7 @@ void UVSphereTest::withoutTextureCoords() { }), TestSuite::Compare::Container); } -void UVSphereTest::withTextureCoords() { +void UVSphereTest::solidWithTextureCoords() { Trade::MeshData3D sphere = UVSphere::solid(3, 3, UVSphere::TextureCoords::Generate); CORRADE_COMPARE_AS(sphere.positions(0), (std::vector{ @@ -124,6 +126,48 @@ void UVSphereTest::withTextureCoords() { }), TestSuite::Compare::Container); } +void UVSphereTest::wireframe() { + Trade::MeshData3D sphere = UVSphere::wireframe(4, 8); + + CORRADE_COMPARE_AS(sphere.positions(0), (std::vector{ + {0.0f, -1.0f, 0.0f}, + + {0.0f, -0.707107f, 0.707107f}, + {0.707107f, -0.707107f, 0.0f}, + {0.0f, -0.707107f, -0.707107f}, + {-0.707107f, -0.707107f, 0.0f}, + + {0.0f, 0.0f, 1.0f}, + {1.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, -1.0f}, + {-1.0f, 0.0f, 0.0f}, + {0.707107f, 0.0f, 0.707107f}, + {0.707107f, 0.0f, -0.707107f}, + {-0.707107f, 0.0f, -0.707107f}, + {-0.707107f, 0.0f, 0.707107f}, + + {0.0f, 0.707107f, 0.707107f}, + {0.707107f, 0.707107f, 0.0f}, + {0.0f, 0.707107f, -0.707107f}, + {-0.707107f, 0.707107f, 0.0f}, + + {0.0f, 1.0f, 0.0f} + }), TestSuite::Compare::Container); + + CORRADE_COMPARE(sphere.normalArrayCount(), 0); + + CORRADE_COMPARE_AS(sphere.indices(), (std::vector{ + 0, 1, 0, 2, 0, 3, 0, 4, + 1, 5, 2, 6, 3, 7, 4, 8, + + 5, 9, 6, 10, 7, 11, 8, 12, + 9, 6, 10, 7, 11, 8, 12, 5, + + 5, 13, 6, 14, 7, 15, 8, 16, + 13, 17, 14, 17, 15, 17, 16, 17 + }), TestSuite::Compare::Container); +} + }}} CORRADE_TEST_MAIN(Magnum::Primitives::Test::UVSphereTest) diff --git a/src/Primitives/UVSphere.cpp b/src/Primitives/UVSphere.cpp index b4003300f..75e67ba5e 100644 --- a/src/Primitives/UVSphere.cpp +++ b/src/Primitives/UVSphere.cpp @@ -26,6 +26,7 @@ #include "Math/Vector3.h" #include "Primitives/Implementation/Spheroid.h" +#include "Primitives/Implementation/WireframeSpheroid.h" #include "Trade/MeshData3D.h" namespace Magnum { namespace Primitives { @@ -57,4 +58,17 @@ Trade::MeshData3D UVSphere::solid(UnsignedInt rings, UnsignedInt segments, Textu return sphere.finalize(); } +Trade::MeshData3D UVSphere::wireframe(const UnsignedInt rings, const UnsignedInt segments) { + CORRADE_ASSERT(rings >= 2 && rings%2 == 0 && segments >= 4 && segments%2 == 0, "Primitives::UVSphere::wireframe(): improper parameters", Trade::MeshData3D(Mesh::Primitive::Lines, {}, {}, {}, {})); + + Implementation::WireframeSpheroid sphere(segments/4); + + /* Make sphere */ + sphere.bottomHemisphere(0.0f, rings/2); + sphere.ring(0.0f); + sphere.topHemisphere(0.0f, rings/2); + + return sphere.finalize(); +} + }} diff --git a/src/Primitives/UVSphere.h b/src/Primitives/UVSphere.h index 7cfb75b94..df0fcdda5 100644 --- a/src/Primitives/UVSphere.h +++ b/src/Primitives/UVSphere.h @@ -59,6 +59,17 @@ class MAGNUM_PRIMITIVES_EXPORT UVSphere { * vertices of one segment are duplicated for texture wrapping. */ static Trade::MeshData3D solid(UnsignedInt rings, UnsignedInt segments, TextureCoords textureCoords = TextureCoords::DontGenerate); + + /** + * @brief Wireframe UV sphere + * @param rings Number of (line) rings. Must be larger or equal + * to 2 and multiple of 2. + * @param segments Number of (line) segments. Must be larger or + * equal to 4 and multiple of 4. + * + * Indexed @ref Mesh::Primitive "Lines". + */ + static Trade::MeshData3D wireframe(UnsignedInt rings, UnsignedInt segments); }; }}