diff --git a/src/Primitives/CMakeLists.txt b/src/Primitives/CMakeLists.txt index 28d2aa07d..e554119cb 100644 --- a/src/Primitives/CMakeLists.txt +++ b/src/Primitives/CMakeLists.txt @@ -1,12 +1,14 @@ set(MagnumPrimitives_SRCS Capsule.cpp Cube.cpp + Cylinder.cpp Icosphere.cpp Plane.cpp UVSphere.cpp) set(MagnumPrimitives_HEADERS Capsule.h Cube.h + Cylinder.h Icosphere.h Plane.h UVSphere.h) diff --git a/src/Primitives/Capsule.cpp b/src/Primitives/Capsule.cpp index 30b5f5def..24b9967d4 100644 --- a/src/Primitives/Capsule.cpp +++ b/src/Primitives/Capsule.cpp @@ -118,14 +118,14 @@ void Capsule::bottomFaceRing() { } } -void Capsule::faceRings(unsigned int count) { +void Capsule::faceRings(unsigned int count, unsigned int offset) { unsigned int vertexSegments = segments + (textureCoords == TextureCoords::Generate ? 1 : 0); for(unsigned int i = 0; i != count; ++i) { for(unsigned int j = 0; j != segments; ++j) { - unsigned int bottomLeft = i*vertexSegments+j+1; + unsigned int bottomLeft = i*vertexSegments+j+offset; unsigned int bottomRight = ((j != segments-1 || textureCoords == TextureCoords::Generate) ? - i*vertexSegments+j+2 : i*segments+1); + i*vertexSegments+j+1+offset : i*segments+offset); unsigned int topLeft = bottomLeft+vertexSegments; unsigned int topRight = bottomRight+vertexSegments; diff --git a/src/Primitives/Capsule.h b/src/Primitives/Capsule.h index f487a9ae5..9bb2820d1 100644 --- a/src/Primitives/Capsule.h +++ b/src/Primitives/Capsule.h @@ -30,6 +30,7 @@ Cylinder along Y axis with hemispheres instead of caps. */ class Capsule: public Trade::MeshData { friend class UVSphere; + friend class Cylinder; public: /** @brief Whether to generate texture coordinates */ @@ -60,7 +61,7 @@ class Capsule: public Trade::MeshData { void hemisphereVertexRings(unsigned int count, GLfloat centerY, GLfloat startRingAngle, GLfloat ringAngleIncrement, GLfloat startTextureCoordsV, GLfloat textureCoordsVIncrement); void cylinderVertexRings(unsigned int count, GLfloat startY, GLfloat yIncrement, GLfloat startTextureCoordsV, GLfloat textureCoordsVIncrement); void bottomFaceRing(); - void faceRings(unsigned int count); + void faceRings(unsigned int count, unsigned int offset = 1); void topFaceRing(); unsigned int segments; diff --git a/src/Primitives/Cylinder.cpp b/src/Primitives/Cylinder.cpp new file mode 100644 index 000000000..19c85f8b1 --- /dev/null +++ b/src/Primitives/Cylinder.cpp @@ -0,0 +1,69 @@ +/* + 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 "Cylinder.h" + +using namespace std; + +namespace Magnum { namespace Primitives { + +Cylinder::Cylinder(unsigned int rings, unsigned int segments, GLfloat length, Flags flags): Capsule(segments, flags & Flag::GenerateTextureCoords ? TextureCoords::Generate : TextureCoords::DontGenerate) { + CORRADE_ASSERT(rings >= 1 && segments >= 3, "Cylinder must have at least one ring and three segments", ); + + GLfloat y = length*0.5f; + GLfloat textureCoordsV = flags & Flag::CapEnds ? 1.0f/(length+2.0f) : 0.0f; + + /* Bottom cap */ + if(flags & Flag::CapEnds) { + capVertex(-y, -1.0f, 0.0f); + capVertexRing(-y, textureCoordsV, Vector3::yAxis(-1.0f)); + } + + /* Vertex rings */ + cylinderVertexRings(rings+1, -y, length/rings, textureCoordsV, length/(rings*(flags & Flag::CapEnds ? length + 2.0f : length))); + + /* Top cap */ + if(flags & Flag::CapEnds) { + capVertexRing(y, 1.0f - textureCoordsV, Vector3::yAxis(1.0f)); + capVertex(y, 1.0f, 1.0f); + } + + /* Faces */ + if(flags & Flag::CapEnds) bottomFaceRing(); + faceRings(rings, flags & Flag::CapEnds ? 1 : 0); + if(flags & Flag::CapEnds) topFaceRing(); +} + +void Cylinder::capVertexRing(GLfloat y, GLfloat textureCoordsV, const Vector3& normal) { + GLfloat segmentAngleIncrement = 2*Math::Constants::pi()/segments; + + for(unsigned int i = 0; i != segments; ++i) { + GLfloat segmentAngle = i*segmentAngleIncrement; + positions(0)->push_back({sin(segmentAngle), y, cos(segmentAngle)}); + normals(0)->push_back(normal); + + if(textureCoords == TextureCoords::Generate) + textureCoords2D(0)->push_back({i*1.0f/segments, textureCoordsV}); + } + + /* Duplicate first segment in the ring for additional vertex for texture coordinate */ + if(textureCoords == TextureCoords::Generate) { + positions(0)->push_back((*positions(0))[positions(0)->size()-segments]); + normals(0)->push_back(normal); + textureCoords2D(0)->push_back({1.0f, textureCoordsV}); + } +} + +}} diff --git a/src/Primitives/Cylinder.h b/src/Primitives/Cylinder.h new file mode 100644 index 000000000..260b3c021 --- /dev/null +++ b/src/Primitives/Cylinder.h @@ -0,0 +1,65 @@ +#ifndef Magnum_Primitives_Cylinder_h +#define Magnum_Primitives_Cylinder_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::Primitives::UVSphere + */ + +#include "Primitives/Capsule.h" +#include + +namespace Magnum { namespace Primitives { + +/** @brief Cylinder primitive */ +class Cylinder: public Capsule { + public: + /** + * @brief %Flags + * + * @see Flags, Cylinder() + */ + enum class Flag { + GenerateTextureCoords = 1, /**< @brief Generate texture coordinates */ + CapEnds /**< @brief Cap ends */ + }; + + /** @brief %Flags */ + typedef Corrade::Containers::EnumSet Flags; + + /** + * @brief Constructor + * @param rings Number of (face) rings. Must be larger or + * equal to 1. + * @param segments Number of (face) segments. Must be larger or + * equal to 3. + * @param length Cylinder length + * @param flags Flags + * + * If texture coordinates are generated, vertices of one segment are + * duplicated for texture wrapping. + */ + Cylinder(unsigned int rings, unsigned int segments, GLfloat length, Flags flags = Flags()); + + private: + void capVertexRing(GLfloat y, GLfloat textureCoordsV, const Vector3& normal); +}; + +CORRADE_ENUMSET_OPERATORS(Cylinder::Flags) + +}} + +#endif diff --git a/src/Primitives/Test/CMakeLists.txt b/src/Primitives/Test/CMakeLists.txt index e30186313..c8374ccd0 100644 --- a/src/Primitives/Test/CMakeLists.txt +++ b/src/Primitives/Test/CMakeLists.txt @@ -1,2 +1,3 @@ corrade_add_test2(PrimitivesCapsuleTest CapsuleTest.cpp LIBRARIES MagnumPrimitives) corrade_add_test2(PrimitivesUVSphereTest UVSphereTest.cpp LIBRARIES MagnumPrimitives) +corrade_add_test2(PrimitivesCylinderTest CylinderTest.cpp LIBRARIES MagnumPrimitives) diff --git a/src/Primitives/Test/CylinderTest.cpp b/src/Primitives/Test/CylinderTest.cpp new file mode 100644 index 000000000..28a89641b --- /dev/null +++ b/src/Primitives/Test/CylinderTest.cpp @@ -0,0 +1,175 @@ +/* + 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 "CylinderTest.h" + +#include + +#include "Primitives/Cylinder.h" + +using namespace std; +using Corrade::TestSuite::Compare::Container; + +CORRADE_TEST_MAIN(Magnum::Primitives::Test::CylinderTest) + +namespace Magnum { namespace Primitives { namespace Test { + +CylinderTest::CylinderTest() { + addTests(&CylinderTest::withoutAnything, + &CylinderTest::withTextureCoordsAndCaps); +} + +void CylinderTest::withoutAnything() { + Cylinder cylinder(2, 3, 3.0f); + + CORRADE_COMPARE_AS(*cylinder.positions(0), (vector{ + {0.0f, -1.5f, 1.0f}, + {0.866025f, -1.5f, -0.5f}, + {-0.866025f, -1.5f, -0.5f}, + + {0.0f, 0.0f, 1.0f}, + {0.866025f, 0.0f, -0.5f}, + {-0.866025f, 0.0f, -0.5f}, + + {0.0f, 1.5f, 1.0f}, + {0.866025f, 1.5f, -0.5f}, + {-0.866025f, 1.5f, -0.5f} + }), Container); + + CORRADE_COMPARE_AS(*cylinder.normals(0), (vector{ + {0.0f, 0.0f, 1.0f}, + {0.866025f, 0.0f, -0.5f}, + {-0.866025f, 0.0f, -0.5f}, + + {0.0f, 0.0f, 1.0f}, + {0.866025f, 0.0f, -0.5f}, + {-0.866025f, 0.0f, -0.5f}, + + {0.0f, 0.0f, 1.0f}, + {0.866025f, 0.0f, -0.5f}, + {-0.866025f, 0.0f, -0.5f} + }), Container); + + CORRADE_COMPARE_AS(*cylinder.indices(), (vector{ + 0, 1, 4, 0, 4, 3, 1, 2, 5, 1, 5, 4, 2, 0, 3, 2, 3, 5, + 3, 4, 7, 3, 7, 6, 4, 5, 8, 4, 8, 7, 5, 3, 6, 5, 6, 8 + }), Container); +} + +void CylinderTest::withTextureCoordsAndCaps() { + Cylinder cylinder(2, 3, 3.0f, Cylinder::Flag::GenerateTextureCoords|Cylinder::Flag::CapEnds); + + CORRADE_COMPARE_AS(*cylinder.positions(0), (vector{ + {0.0f, -1.5f, 0.0f}, + + {0.0f, -1.5f, 1.0f}, + {0.866025f, -1.5f, -0.5f}, + {-0.866025f, -1.5f, -0.5f}, + {0.0f, -1.5f, 1.0f}, + + {0.0f, -1.5f, 1.0f}, + {0.866025f, -1.5f, -0.5f}, + {-0.866025f, -1.5f, -0.5f}, + {0.0f, -1.5f, 1.0f}, + + {0.0f, 0.0f, 1.0f}, + {0.866025f, 0.0f, -0.5f}, + {-0.866025f, 0.0f, -0.5f}, + {0.0f, 0.0f, 1.0f}, + + {0.0f, 1.5f, 1.0f}, + {0.866025f, 1.5f, -0.5f}, + {-0.866025f, 1.5f, -0.5f}, + {0.0f, 1.5f, 1.0f}, + + {0.0f, 1.5f, 1.0f}, + {0.866025f, 1.5f, -0.5f}, + {-0.866025f, 1.5f, -0.5f}, + {0.0f, 1.5f, 1.0f}, + + {0.0f, 1.5f, 0.0f} + }), Container); + + CORRADE_COMPARE_AS(*cylinder.normals(0), (vector{ + {0.0f, -1.0f, 0.0f}, + + {0.0f, -1.0f, 0.0f}, + {0.0f, -1.0f, 0.0f}, + {0.0f, -1.0f, 0.0f}, + {0.0f, -1.0f, 0.0f}, + + {0.0f, 0.0f, 1.0f}, + {0.866025f, 0.0f, -0.5f}, + {-0.866025f, 0.0f, -0.5f}, + {0.0f, 0.0f, 1.0f}, + + {0.0f, 0.0f, 1.0f}, + {0.866025f, 0.0f, -0.5f}, + {-0.866025f, 0.0f, -0.5f}, + {0.0f, 0.0f, 1.0f}, + + {0.0f, 0.0f, 1.0f}, + {0.866025f, 0.0f, -0.5f}, + {-0.866025f, 0.0f, -0.5f}, + {0.0f, 0.0f, 1.0f}, + + {0.0f, 1.0f, 0.0f}, + {0.0f, 1.0f, 0.0f}, + {0.0f, 1.0f, 0.0f}, + {0.0f, 1.0f, 0.0f}, + + {0.0f, 1.0f, 0.0f}, + }), Container); + + CORRADE_COMPARE_AS(*cylinder.textureCoords2D(0), (vector{ + {0.5f, 0.0f}, + + {0.0f, 0.2f}, + {0.333333f, 0.2f}, + {0.666667f, 0.2f}, + {1.0f, 0.2f}, + + {0.0f, 0.2f}, + {0.333333f, 0.2f}, + {0.666667f, 0.2f}, + {1.0f, 0.2f}, + + {0.0f, 0.5f}, + {0.333333f, 0.5f}, + {0.666667f, 0.5f}, + {1.0f, 0.5f}, + + {0.0f, 0.8f}, + {0.333333f, 0.8f}, + {0.666667f, 0.8f}, + {1.0f, 0.8f}, + + {0.0f, 0.8f}, + {0.333333f, 0.8f}, + {0.666667f, 0.8f}, + {1.0f, 0.8f}, + + {0.5f, 1.0f} + }), Container); + + CORRADE_COMPARE_AS(*cylinder.indices(), (vector{ + 0, 2, 1, 0, 3, 2, 0, 4, 3, + 1, 2, 6, 1, 6, 5, 2, 3, 7, 2, 7, 6, 3, 4, 8, 3, 8, 7, + 5, 6, 10, 5, 10, 9, 6, 7, 11, 6, 11, 10, 7, 8, 12, 7, + 12, 11, 17, 18, 21, 18, 19, 21, 19, 20, 21 + }), Container); +} + +}}} diff --git a/src/Primitives/Test/CylinderTest.h b/src/Primitives/Test/CylinderTest.h new file mode 100644 index 000000000..0d575bc23 --- /dev/null +++ b/src/Primitives/Test/CylinderTest.h @@ -0,0 +1,32 @@ +#ifndef Magnum_Primitives_Test_CylinderTest_h +#define Magnum_Primitives_Test_CylinderTest_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 + +namespace Magnum { namespace Primitives { namespace Test { + +class CylinderTest: public Corrade::TestSuite::Tester { + public: + CylinderTest(); + + void withoutAnything(); + void withTextureCoordsAndCaps(); +}; + +}}} + +#endif