diff --git a/src/Primitives/CMakeLists.txt b/src/Primitives/CMakeLists.txt index 9675469aa..01224000d 100644 --- a/src/Primitives/CMakeLists.txt +++ b/src/Primitives/CMakeLists.txt @@ -32,7 +32,9 @@ set(MagnumPrimitives_SRCS Line.cpp Plane.cpp Square.cpp - UVSphere.cpp) + UVSphere.cpp + + Implementation/Spheroid.cpp) set(MagnumPrimitives_HEADERS Capsule.h diff --git a/src/Primitives/Capsule.cpp b/src/Primitives/Capsule.cpp index e35ecb34f..1447e2e16 100644 --- a/src/Primitives/Capsule.cpp +++ b/src/Primitives/Capsule.cpp @@ -24,147 +24,43 @@ #include "Capsule.h" -#include "Math/Functions.h" -#include "Math/Vector3.h" +#include "Math/Angle.h" +#include "Primitives/Implementation/Spheroid.h" namespace Magnum { namespace Primitives { -Capsule::Capsule(UnsignedInt hemisphereRings, UnsignedInt cylinderRings, UnsignedInt segments, Float length, TextureCoords textureCoords): MeshData3D(Mesh::Primitive::Triangles, new std::vector, {new std::vector()}, {new std::vector()}, textureCoords == TextureCoords::Generate ? std::vector*>{new std::vector()} : std::vector*>()), segments(segments), textureCoords(textureCoords) { - CORRADE_ASSERT(hemisphereRings >= 1 && cylinderRings >= 1 && segments >= 3, "Capsule must have at least one hemisphere ring, one cylinder ring and three segments", ); +Trade::MeshData3D Capsule::solid(UnsignedInt hemisphereRings, UnsignedInt cylinderRings, UnsignedInt segments, Float length, TextureCoords textureCoords) { + CORRADE_ASSERT(hemisphereRings >= 1 && cylinderRings >= 1 && segments >= 3, "Capsule must have at least one hemisphere ring, one cylinder ring and three segments", Trade::MeshData3D(Mesh::Primitive::Triangles, nullptr, {}, {}, {})); + + Implementation::Spheroid capsule(segments, textureCoords == TextureCoords::Generate ? + Implementation::Spheroid::TextureCoords::Generate : + Implementation::Spheroid::TextureCoords::DontGenerate); Float height = 2.0f+length; Float hemisphereTextureCoordsVIncrement = 1.0f/(hemisphereRings*height); Rad hemisphereRingAngleIncrement(Constants::pi()/(2*hemisphereRings)); /* Bottom cap vertex */ - capVertex(-height/2, -1.0f, 0.0f); + capsule.capVertex(-height/2, -1.0f, 0.0f); /* Rings of bottom hemisphere */ - hemisphereVertexRings(hemisphereRings-1, -length/2, -Rad(Constants::pi())/2+hemisphereRingAngleIncrement, hemisphereRingAngleIncrement, hemisphereTextureCoordsVIncrement, hemisphereTextureCoordsVIncrement); + capsule.hemisphereVertexRings(hemisphereRings-1, -length/2, -Rad(Constants::pi())/2+hemisphereRingAngleIncrement, hemisphereRingAngleIncrement, hemisphereTextureCoordsVIncrement, hemisphereTextureCoordsVIncrement); /* Rings of cylinder */ - cylinderVertexRings(cylinderRings+1, -length/2, length/cylinderRings, 1.0f/height, length/(cylinderRings*height)); + capsule.cylinderVertexRings(cylinderRings+1, -length/2, length/cylinderRings, 1.0f/height, length/(cylinderRings*height)); /* Rings of top hemisphere */ - hemisphereVertexRings(hemisphereRings-1, length/2, hemisphereRingAngleIncrement, hemisphereRingAngleIncrement, (1.0f + length)/height+hemisphereTextureCoordsVIncrement, hemisphereTextureCoordsVIncrement); + capsule.hemisphereVertexRings(hemisphereRings-1, length/2, hemisphereRingAngleIncrement, hemisphereRingAngleIncrement, (1.0f + length)/height+hemisphereTextureCoordsVIncrement, hemisphereTextureCoordsVIncrement); /* Top cap vertex */ - capVertex(height/2, 1.0f, 1.0f); + capsule.capVertex(height/2, 1.0f, 1.0f); /* Faces */ - bottomFaceRing(); - faceRings(hemisphereRings*2-2+cylinderRings); - topFaceRing(); -} - -Capsule::Capsule(UnsignedInt segments, TextureCoords textureCoords): MeshData3D(Mesh::Primitive::Triangles, new std::vector, {new std::vector()}, {new std::vector()}, textureCoords == TextureCoords::Generate ? std::vector*>{new std::vector()} : std::vector*>()), segments(segments), textureCoords(textureCoords) {} - -void Capsule::capVertex(Float y, Float normalY, Float textureCoordsV) { - positions(0)->push_back({0.0f, y, 0.0f}); - normals(0)->push_back({0.0f, normalY, 0.0f}); - - if(textureCoords == TextureCoords::Generate) - textureCoords2D(0)->push_back({0.5, textureCoordsV}); -} - -void Capsule::hemisphereVertexRings(UnsignedInt count, Float centerY, Rad startRingAngle, Rad ringAngleIncrement, Float startTextureCoordsV, Float textureCoordsVIncrement) { - Rad segmentAngleIncrement(2*Constants::pi()/segments); - Float x, y, z; - for(UnsignedInt i = 0; i != count; ++i) { - Rad ringAngle = startRingAngle + i*ringAngleIncrement; - x = z = Math::cos(ringAngle); - y = Math::sin(ringAngle); - - for(UnsignedInt j = 0; j != segments; ++j) { - Rad segmentAngle = j*segmentAngleIncrement; - positions(0)->push_back({x*Math::sin(segmentAngle), centerY+y, z*Math::cos(segmentAngle)}); - normals(0)->push_back({x*Math::sin(segmentAngle), y, z*Math::cos(segmentAngle)}); - - if(textureCoords == TextureCoords::Generate) - textureCoords2D(0)->push_back({j*1.0f/segments, startTextureCoordsV + i*textureCoordsVIncrement}); - } - - /* 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((*normals(0))[normals(0)->size()-segments]); - textureCoords2D(0)->push_back({1.0f, startTextureCoordsV + i*textureCoordsVIncrement}); - } - } -} - -void Capsule::cylinderVertexRings(UnsignedInt count, Float startY, Float yIncrement, Float startTextureCoordsV, Float textureCoordsVIncrement) { - Rad segmentAngleIncrement(2*Constants::pi()/segments); - for(UnsignedInt i = 0; i != count; ++i) { - for(UnsignedInt j = 0; j != segments; ++j) { - Rad segmentAngle = j*segmentAngleIncrement; - positions(0)->push_back({Math::sin(segmentAngle), startY, Math::cos(segmentAngle)}); - normals(0)->push_back({Math::sin(segmentAngle), 0.0f, Math::cos(segmentAngle)}); - - if(textureCoords == TextureCoords::Generate) - textureCoords2D(0)->push_back({j*1.0f/segments, startTextureCoordsV + i*textureCoordsVIncrement}); - } - - /* 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((*normals(0))[normals(0)->size()-segments]); - textureCoords2D(0)->push_back({1.0f, startTextureCoordsV + i*textureCoordsVIncrement}); - } - - startY += yIncrement; - } -} - -void Capsule::bottomFaceRing() { - for(UnsignedInt j = 0; j != segments; ++j) { - /* Bottom vertex */ - indices()->push_back(0); - - /* Top right vertex */ - indices()->push_back((j != segments-1 || textureCoords == TextureCoords::Generate) ? - j+2 : 1); - - /* Top left vertex */ - indices()->push_back(j+1); - } -} - -void Capsule::faceRings(UnsignedInt count, UnsignedInt offset) { - UnsignedInt vertexSegments = segments + (textureCoords == TextureCoords::Generate ? 1 : 0); - - for(UnsignedInt i = 0; i != count; ++i) { - for(UnsignedInt j = 0; j != segments; ++j) { - UnsignedInt bottomLeft = i*vertexSegments+j+offset; - UnsignedInt bottomRight = ((j != segments-1 || textureCoords == TextureCoords::Generate) ? - i*vertexSegments+j+1+offset : i*segments+offset); - UnsignedInt topLeft = bottomLeft+vertexSegments; - UnsignedInt topRight = bottomRight+vertexSegments; - - indices()->push_back(bottomLeft); - indices()->push_back(bottomRight); - indices()->push_back(topRight); - indices()->push_back(bottomLeft); - indices()->push_back(topRight); - indices()->push_back(topLeft); - } - } -} - -void Capsule::topFaceRing() { - UnsignedInt vertexSegments = segments + (textureCoords == TextureCoords::Generate ? 1 : 0); - - for(UnsignedInt j = 0; j != segments; ++j) { - /* Bottom left vertex */ - indices()->push_back(normals(0)->size()-vertexSegments+j-1); - - /* Bottom right vertex */ - indices()->push_back((j != segments-1 || textureCoords == TextureCoords::Generate) ? - normals(0)->size()-vertexSegments+j : normals(0)->size()-segments-1); + capsule.bottomFaceRing(); + capsule.faceRings(hemisphereRings*2-2+cylinderRings); + capsule.topFaceRing(); - /* Top vertex */ - indices()->push_back(normals(0)->size()-1); - } + return std::move(capsule); } }} diff --git a/src/Primitives/Capsule.h b/src/Primitives/Capsule.h index 00111ae74..fbefe1c30 100644 --- a/src/Primitives/Capsule.h +++ b/src/Primitives/Capsule.h @@ -28,56 +28,40 @@ * @brief Class Magnum::Primitives::Capsule */ -#include "Trade/MeshData3D.h" - #include "Primitives/magnumPrimitivesVisibility.h" +#include "Trade/Trade.h" namespace Magnum { namespace Primitives { /** @brief 3D capsule primitive -%Cylinder along Y axis with hemispheres instead of caps. Indexed @ref Mesh::Primitive "Triangles" -with normals and optional 2D texture coordinates. +%Cylinder of radius `1` along Y axis with hemispheres instead of caps. */ -class Capsule: public Trade::MeshData3D { - friend class UVSphere; - friend class Cylinder; - +class MAGNUM_PRIMITIVES_EXPORT Capsule { public: /** @brief Whether to generate texture coordinates */ - enum class TextureCoords { + enum class TextureCoords: UnsignedByte { Generate, /**< Generate texture coordinates */ DontGenerate /**< Don't generate texture coordinates */ }; /** - * @brief Constructor + * @brief Solid capsule * @param hemisphereRings Number of (face) rings for each hemisphere. * Must be larger or equal to 1. * @param cylinderRings Number of (face) rings for cylinder. Must be * larger or equal to 1. - * @param segments Number of (face) segments. Must be larger or equal to 3. + * @param segments Number of (face) segments. Must be larger or + * equal to 3. * @param length Length of the capsule, excluding hemispheres. * @param textureCoords Whether to generate texture coordinates. * - * If texture coordinates are generated, vertices of one segment are - * duplicated for texture wrapping. + * Indexed @ref Mesh::Primitive "Triangles" with normals and optional + * 2D texture coordinates. If texture coordinates are generated, + * vertices of one segment are duplicated for texture wrapping. */ - explicit MAGNUM_PRIMITIVES_EXPORT Capsule(UnsignedInt hemisphereRings, UnsignedInt cylinderRings, UnsignedInt segments, Float length, TextureCoords textureCoords = TextureCoords::DontGenerate); - - private: - Capsule(UnsignedInt segments, TextureCoords textureCoords); - - void capVertex(Float y, Float normalY, Float textureCoordsV); - void hemisphereVertexRings(UnsignedInt count, Float centerY, Rad startRingAngle, Rad ringAngleIncrement, Float startTextureCoordsV, Float textureCoordsVIncrement); - void cylinderVertexRings(UnsignedInt count, Float startY, Float yIncrement, Float startTextureCoordsV, Float textureCoordsVIncrement); - void bottomFaceRing(); - void faceRings(UnsignedInt count, UnsignedInt offset = 1); - void topFaceRing(); - - UnsignedInt segments; - TextureCoords textureCoords; + static Trade::MeshData3D solid(UnsignedInt hemisphereRings, UnsignedInt cylinderRings, UnsignedInt segments, Float length, TextureCoords textureCoords = TextureCoords::DontGenerate); }; }} diff --git a/src/Primitives/Circle.h b/src/Primitives/Circle.h index a27876e31..b97c1e7b2 100644 --- a/src/Primitives/Circle.h +++ b/src/Primitives/Circle.h @@ -37,7 +37,7 @@ namespace Magnum { namespace Primitives { /** @brief 2D circle primitive -Circle with radius 1. +Circle with radius `1`. */ class MAGNUM_PRIMITIVES_EXPORT Circle { public: diff --git a/src/Primitives/Cylinder.cpp b/src/Primitives/Cylinder.cpp index 691e5402b..13c043b3a 100644 --- a/src/Primitives/Cylinder.cpp +++ b/src/Primitives/Cylinder.cpp @@ -24,56 +24,40 @@ #include "Cylinder.h" -#include "Math/Functions.h" #include "Math/Vector3.h" +#include "Primitives/Implementation/Spheroid.h" namespace Magnum { namespace Primitives { -Cylinder::Cylinder(UnsignedInt rings, UnsignedInt segments, Float 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", ); +Trade::MeshData3D Cylinder::solid(UnsignedInt rings, UnsignedInt segments, Float length, Cylinder::Flags flags) { + CORRADE_ASSERT(rings >= 1 && segments >= 3, "Primitives::Cylinder::solid(): cylinder must have at least one ring and three segments", Trade::MeshData3D(Mesh::Primitive::Triangles, nullptr, {}, {}, {})); + + Implementation::Spheroid cylinder(segments, flags & Flag::GenerateTextureCoords ? Implementation::Spheroid::TextureCoords::Generate : Implementation::Spheroid::TextureCoords::DontGenerate); Float y = length*0.5f; Float 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)); + cylinder.capVertex(-y, -1.0f, 0.0f); + cylinder.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))); + cylinder.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); + cylinder.capVertexRing(y, 1.0f - textureCoordsV, Vector3::yAxis(1.0f)); + cylinder.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(Float y, Float textureCoordsV, const Vector3& normal) { - Rad segmentAngleIncrement(2*Constants::pi()/segments); - - for(UnsignedInt i = 0; i != segments; ++i) { - Rad segmentAngle = i*segmentAngleIncrement; - positions(0)->push_back({Math::sin(segmentAngle), y, Math::cos(segmentAngle)}); - normals(0)->push_back(normal); + if(flags & Flag::CapEnds) cylinder.bottomFaceRing(); + cylinder.faceRings(rings, flags & Flag::CapEnds ? 1 : 0); + if(flags & Flag::CapEnds) cylinder.topFaceRing(); - 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}); - } + return std::move(cylinder); } }} diff --git a/src/Primitives/Cylinder.h b/src/Primitives/Cylinder.h index eac16de9d..ed38da5ae 100644 --- a/src/Primitives/Cylinder.h +++ b/src/Primitives/Cylinder.h @@ -30,19 +30,17 @@ #include -#include "Primitives/Capsule.h" - #include "Primitives/magnumPrimitivesVisibility.h" +#include "Trade/Trade.h" namespace Magnum { namespace Primitives { /** @brief 3D cylinder primitive -Indexed @ref Mesh::Primitive "Triangles" with normals, optional 2D texture -coordinates and optional capped ends. +%Cylinder along Y axis of radius `1`. */ -class Cylinder: public Capsule { +class MAGNUM_PRIMITIVES_EXPORT Cylinder { public: /** * @brief %Flags @@ -58,7 +56,7 @@ class Cylinder: public Capsule { typedef Corrade::Containers::EnumSet Flags; /** - * @brief Constructor + * @brief Solid cylinder * @param rings Number of (face) rings. Must be larger or * equal to 1. * @param segments Number of (face) segments. Must be larger or @@ -66,13 +64,12 @@ class Cylinder: public Capsule { * @param length Cylinder length * @param flags Flags * - * If texture coordinates are generated, vertices of one segment are - * duplicated for texture wrapping. + * Indexed @ref Mesh::Primitive "Triangles" with normals, optional 2D + * texture coordinates and optional capped ends. If texture coordinates + * are generated, vertices of one segment are duplicated for texture + * wrapping. */ - explicit MAGNUM_PRIMITIVES_EXPORT Cylinder(UnsignedInt rings, UnsignedInt segments, Float length, Flags flags = Flags()); - - private: - void capVertexRing(Float y, Float textureCoordsV, const Vector3& normal); + static Trade::MeshData3D solid(UnsignedInt rings, UnsignedInt segments, Float length, Flags flags = Flags()); }; CORRADE_ENUMSET_OPERATORS(Cylinder::Flags) diff --git a/src/Primitives/Implementation/Spheroid.cpp b/src/Primitives/Implementation/Spheroid.cpp new file mode 100644 index 000000000..288279160 --- /dev/null +++ b/src/Primitives/Implementation/Spheroid.cpp @@ -0,0 +1,162 @@ +/* + 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 "Spheroid.h" + +#include "Math/Functions.h" +#include "Math/Vector3.h" + +namespace Magnum { namespace Primitives { namespace Implementation { + +Spheroid::Spheroid(UnsignedInt segments, TextureCoords textureCoords): MeshData3D(Mesh::Primitive::Triangles, new std::vector, {new std::vector()}, {new std::vector()}, textureCoords == TextureCoords::Generate ? std::vector*>{new std::vector()} : std::vector*>()), segments(segments), textureCoords(textureCoords) {} + +void Spheroid::capVertex(Float y, Float normalY, Float textureCoordsV) { + positions(0)->push_back({0.0f, y, 0.0f}); + normals(0)->push_back({0.0f, normalY, 0.0f}); + + if(textureCoords == TextureCoords::Generate) + textureCoords2D(0)->push_back({0.5, textureCoordsV}); +} + +void Spheroid::hemisphereVertexRings(UnsignedInt count, Float centerY, Rad startRingAngle, Rad ringAngleIncrement, Float startTextureCoordsV, Float textureCoordsVIncrement) { + Rad segmentAngleIncrement(2*Constants::pi()/segments); + Float x, y, z; + for(UnsignedInt i = 0; i != count; ++i) { + Rad ringAngle = startRingAngle + i*ringAngleIncrement; + x = z = Math::cos(ringAngle); + y = Math::sin(ringAngle); + + for(UnsignedInt j = 0; j != segments; ++j) { + Rad segmentAngle = j*segmentAngleIncrement; + positions(0)->push_back({x*Math::sin(segmentAngle), centerY+y, z*Math::cos(segmentAngle)}); + normals(0)->push_back({x*Math::sin(segmentAngle), y, z*Math::cos(segmentAngle)}); + + if(textureCoords == TextureCoords::Generate) + textureCoords2D(0)->push_back({j*1.0f/segments, startTextureCoordsV + i*textureCoordsVIncrement}); + } + + /* 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((*normals(0))[normals(0)->size()-segments]); + textureCoords2D(0)->push_back({1.0f, startTextureCoordsV + i*textureCoordsVIncrement}); + } + } +} + +void Spheroid::cylinderVertexRings(UnsignedInt count, Float startY, Float yIncrement, Float startTextureCoordsV, Float textureCoordsVIncrement) { + Rad segmentAngleIncrement(2*Constants::pi()/segments); + for(UnsignedInt i = 0; i != count; ++i) { + for(UnsignedInt j = 0; j != segments; ++j) { + Rad segmentAngle = j*segmentAngleIncrement; + positions(0)->push_back({Math::sin(segmentAngle), startY, Math::cos(segmentAngle)}); + normals(0)->push_back({Math::sin(segmentAngle), 0.0f, Math::cos(segmentAngle)}); + + if(textureCoords == TextureCoords::Generate) + textureCoords2D(0)->push_back({j*1.0f/segments, startTextureCoordsV + i*textureCoordsVIncrement}); + } + + /* 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((*normals(0))[normals(0)->size()-segments]); + textureCoords2D(0)->push_back({1.0f, startTextureCoordsV + i*textureCoordsVIncrement}); + } + + startY += yIncrement; + } +} + +void Spheroid::bottomFaceRing() { + for(UnsignedInt j = 0; j != segments; ++j) { + /* Bottom vertex */ + indices()->push_back(0); + + /* Top right vertex */ + indices()->push_back((j != segments-1 || textureCoords == TextureCoords::Generate) ? + j+2 : 1); + + /* Top left vertex */ + indices()->push_back(j+1); + } +} + +void Spheroid::faceRings(UnsignedInt count, UnsignedInt offset) { + UnsignedInt vertexSegments = segments + (textureCoords == TextureCoords::Generate ? 1 : 0); + + for(UnsignedInt i = 0; i != count; ++i) { + for(UnsignedInt j = 0; j != segments; ++j) { + UnsignedInt bottomLeft = i*vertexSegments+j+offset; + UnsignedInt bottomRight = ((j != segments-1 || textureCoords == TextureCoords::Generate) ? + i*vertexSegments+j+1+offset : i*segments+offset); + UnsignedInt topLeft = bottomLeft+vertexSegments; + UnsignedInt topRight = bottomRight+vertexSegments; + + indices()->push_back(bottomLeft); + indices()->push_back(bottomRight); + indices()->push_back(topRight); + indices()->push_back(bottomLeft); + indices()->push_back(topRight); + indices()->push_back(topLeft); + } + } +} + +void Spheroid::topFaceRing() { + UnsignedInt vertexSegments = segments + (textureCoords == TextureCoords::Generate ? 1 : 0); + + for(UnsignedInt j = 0; j != segments; ++j) { + /* Bottom left vertex */ + indices()->push_back(normals(0)->size()-vertexSegments+j-1); + + /* Bottom right vertex */ + indices()->push_back((j != segments-1 || textureCoords == TextureCoords::Generate) ? + normals(0)->size()-vertexSegments+j : normals(0)->size()-segments-1); + + /* Top vertex */ + indices()->push_back(normals(0)->size()-1); + } +} + +void Spheroid::capVertexRing(Float y, Float textureCoordsV, const Vector3& normal) { + Rad segmentAngleIncrement(2*Constants::pi()/segments); + + for(UnsignedInt i = 0; i != segments; ++i) { + Rad segmentAngle = i*segmentAngleIncrement; + positions(0)->push_back({Math::sin(segmentAngle), y, Math::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/Implementation/Spheroid.h b/src/Primitives/Implementation/Spheroid.h new file mode 100644 index 000000000..9a3f89203 --- /dev/null +++ b/src/Primitives/Implementation/Spheroid.h @@ -0,0 +1,54 @@ +#ifndef Magnum_Primitives_Spheroid_h +#define Magnum_Primitives_Spheroid_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 "Trade/MeshData3D.h" + +namespace Magnum { namespace Primitives { namespace Implementation { + +class Spheroid: public Trade::MeshData3D { + public: + enum class TextureCoords: UnsignedByte { + DontGenerate, + Generate + }; + + Spheroid(UnsignedInt segments, TextureCoords textureCoords); + + void capVertex(Float y, Float normalY, Float textureCoordsV); + void hemisphereVertexRings(UnsignedInt count, Float centerY, Rad startRingAngle, Rad ringAngleIncrement, Float startTextureCoordsV, Float textureCoordsVIncrement); + void cylinderVertexRings(UnsignedInt count, Float startY, Float yIncrement, Float startTextureCoordsV, Float textureCoordsVIncrement); + void bottomFaceRing(); + void faceRings(UnsignedInt count, UnsignedInt offset = 1); + void topFaceRing(); + void capVertexRing(Float y, Float textureCoordsV, const Vector3& normal); + + UnsignedInt segments; + TextureCoords textureCoords; +}; + +}}} + +#endif diff --git a/src/Primitives/Test/CapsuleTest.cpp b/src/Primitives/Test/CapsuleTest.cpp index 3d74a2680..56b5bc569 100644 --- a/src/Primitives/Test/CapsuleTest.cpp +++ b/src/Primitives/Test/CapsuleTest.cpp @@ -29,6 +29,7 @@ #include #include "Math/Vector3.h" +#include "Trade/MeshData3D.h" #include "Primitives/Capsule.h" using Corrade::TestSuite::Compare::Container; @@ -49,7 +50,7 @@ CapsuleTest::CapsuleTest() { } void CapsuleTest::withoutTextureCoords() { - Capsule capsule(2, 2, 3, 1.0f); + Trade::MeshData3D capsule = Capsule::solid(2, 2, 3, 1.0f); CORRADE_COMPARE_AS(*capsule.positions(0), (std::vector{ {0.0f, -1.5f, 0.0f}, @@ -114,7 +115,7 @@ void CapsuleTest::withoutTextureCoords() { } void CapsuleTest::withTextureCoords() { - Capsule capsule(2, 2, 3, 1.0f, Capsule::TextureCoords::Generate); + Trade::MeshData3D capsule = Capsule::solid(2, 2, 3, 1.0f, Capsule::TextureCoords::Generate); CORRADE_COMPARE_AS(*capsule.positions(0), (std::vector{ {0.0f, -1.5f, 0.0f}, diff --git a/src/Primitives/Test/CylinderTest.cpp b/src/Primitives/Test/CylinderTest.cpp index 938162fab..ef861c206 100644 --- a/src/Primitives/Test/CylinderTest.cpp +++ b/src/Primitives/Test/CylinderTest.cpp @@ -27,6 +27,7 @@ #include "Math/Vector3.h" #include "Primitives/Cylinder.h" +#include "Trade/MeshData3D.h" using Corrade::TestSuite::Compare::Container; @@ -46,7 +47,7 @@ CylinderTest::CylinderTest() { } void CylinderTest::withoutAnything() { - Cylinder cylinder(2, 3, 3.0f); + Trade::MeshData3D cylinder = Cylinder::solid(2, 3, 3.0f); CORRADE_COMPARE_AS(*cylinder.positions(0), (std::vector{ {0.0f, -1.5f, 1.0f}, @@ -83,7 +84,7 @@ void CylinderTest::withoutAnything() { } void CylinderTest::withTextureCoordsAndCaps() { - Cylinder cylinder(2, 3, 3.0f, Cylinder::Flag::GenerateTextureCoords|Cylinder::Flag::CapEnds); + Trade::MeshData3D cylinder = Cylinder::solid(2, 3, 3.0f, Cylinder::Flag::GenerateTextureCoords|Cylinder::Flag::CapEnds); CORRADE_COMPARE_AS(*cylinder.positions(0), (std::vector{ {0.0f, -1.5f, 0.0f}, diff --git a/src/Primitives/Test/UVSphereTest.cpp b/src/Primitives/Test/UVSphereTest.cpp index beaa6b46f..93cb2ff1f 100644 --- a/src/Primitives/Test/UVSphereTest.cpp +++ b/src/Primitives/Test/UVSphereTest.cpp @@ -27,6 +27,7 @@ #include "Math/Vector3.h" #include "Primitives/UVSphere.h" +#include "Trade/MeshData3D.h" using Corrade::TestSuite::Compare::Container; @@ -46,7 +47,7 @@ UVSphereTest::UVSphereTest() { } void UVSphereTest::withoutTextureCoords() { - UVSphere sphere(3, 3); + Trade::MeshData3D sphere = UVSphere::solid(3, 3); CORRADE_COMPARE_AS(*sphere.positions(0), (std::vector{ {0.0f, -1.0f, 0.0f}, @@ -84,7 +85,7 @@ void UVSphereTest::withoutTextureCoords() { } void UVSphereTest::withTextureCoords() { - UVSphere sphere(3, 3, UVSphere::TextureCoords::Generate); + Trade::MeshData3D sphere = UVSphere::solid(3, 3, UVSphere::TextureCoords::Generate); CORRADE_COMPARE_AS(*sphere.positions(0), (std::vector{ {0.0f, -1.0f, 0.0f}, diff --git a/src/Primitives/UVSphere.cpp b/src/Primitives/UVSphere.cpp index b6cde4ca3..5ec1d72c5 100644 --- a/src/Primitives/UVSphere.cpp +++ b/src/Primitives/UVSphere.cpp @@ -25,28 +25,35 @@ #include "UVSphere.h" #include "Math/Angle.h" +#include "Implementation/Spheroid.h" namespace Magnum { namespace Primitives { -UVSphere::UVSphere(UnsignedInt rings, UnsignedInt segments, TextureCoords textureCoords): Capsule(segments, textureCoords) { - CORRADE_ASSERT(rings >= 2 && segments >= 3, "UVSphere must have at least two rings and three segments", ); +Trade::MeshData3D UVSphere::solid(UnsignedInt rings, UnsignedInt segments, TextureCoords textureCoords) { + CORRADE_ASSERT(rings >= 2 && segments >= 3, "UVSphere must have at least two rings and three segments", Trade::MeshData3D(Mesh::Primitive::Triangles, nullptr, {}, {}, {})); + + Implementation::Spheroid sphere(segments, textureCoords == TextureCoords::Generate ? + Implementation::Spheroid::TextureCoords::Generate : + Implementation::Spheroid::TextureCoords::DontGenerate); Float textureCoordsVIncrement = 1.0f/rings; Rad ringAngleIncrement(Constants::pi()/rings); /* Bottom cap vertex */ - capVertex(-1.0f, -1.0f, 0.0f); + sphere.capVertex(-1.0f, -1.0f, 0.0f); /* Vertex rings */ - hemisphereVertexRings(rings-1, 0.0f, -Rad(Constants::pi())/2+ringAngleIncrement, ringAngleIncrement, textureCoordsVIncrement, textureCoordsVIncrement); + sphere.hemisphereVertexRings(rings-1, 0.0f, -Rad(Constants::pi())/2+ringAngleIncrement, ringAngleIncrement, textureCoordsVIncrement, textureCoordsVIncrement); /* Top cap vertex */ - capVertex(1.0f, 1.0f, 1.0f); + sphere.capVertex(1.0f, 1.0f, 1.0f); /* Faces */ - bottomFaceRing(); - faceRings(rings-2); - topFaceRing(); + sphere.bottomFaceRing(); + sphere.faceRings(rings-2); + sphere.topFaceRing(); + + return std::move(sphere); } }} diff --git a/src/Primitives/UVSphere.h b/src/Primitives/UVSphere.h index 8b6266c45..7cfb75b94 100644 --- a/src/Primitives/UVSphere.h +++ b/src/Primitives/UVSphere.h @@ -28,30 +28,37 @@ * @brief Class Magnum::Primitives::UVSphere */ -#include "Primitives/Capsule.h" - #include "Primitives/magnumPrimitivesVisibility.h" +#include "Trade/Trade.h" namespace Magnum { namespace Primitives { /** @brief 3D UV sphere primitive -Indexed @ref Mesh::Primitive "Triangles" with normals and optional 2D texture -coordinates. +Sphere with radius `1`. */ -class UVSphere: public Capsule { +class MAGNUM_PRIMITIVES_EXPORT UVSphere { public: + /** @brief Whether to generate texture coordinates */ + enum class TextureCoords: UnsignedByte { + Generate, /**< Generate texture coordinates */ + DontGenerate /**< Don't generate texture coordinates */ + }; + /** - * @brief Constructor - * @param rings Number of (face) rings. Must be larger or equal to 2. - * @param segments Number of (face) segments. Must be larger or equal to 3. + * @brief Solid UV sphere + * @param rings Number of (face) rings. Must be larger or equal + * to 2. + * @param segments Number of (face) segments. Must be larger or + * equal to 3. * @param textureCoords Whether to generate texture coordinates. * - * If texture coordinates are generated, vertices of one segment are - * duplicated for texture wrapping. + * Indexed @ref Mesh::Primitive "Triangles" with normals and optional + * 2D texture coordinates. If texture coordinates are generated, + * vertices of one segment are duplicated for texture wrapping. */ - explicit MAGNUM_PRIMITIVES_EXPORT UVSphere(UnsignedInt rings, UnsignedInt segments, TextureCoords textureCoords = TextureCoords::DontGenerate); + static Trade::MeshData3D solid(UnsignedInt rings, UnsignedInt segments, TextureCoords textureCoords = TextureCoords::DontGenerate); }; }}