Browse Source

Primitives: reworked remaining classes to have the same API as the rest.

Now every primitive can be created using `solid()` or `wireframe()`
function returning directly Trade::MeshData.
pull/278/head
Vladimír Vondruš 13 years ago
parent
commit
70ba2bd77c
  1. 4
      src/Primitives/CMakeLists.txt
  2. 138
      src/Primitives/Capsule.cpp
  3. 38
      src/Primitives/Capsule.h
  4. 2
      src/Primitives/Circle.h
  5. 44
      src/Primitives/Cylinder.cpp
  6. 21
      src/Primitives/Cylinder.h
  7. 162
      src/Primitives/Implementation/Spheroid.cpp
  8. 54
      src/Primitives/Implementation/Spheroid.h
  9. 5
      src/Primitives/Test/CapsuleTest.cpp
  10. 5
      src/Primitives/Test/CylinderTest.cpp
  11. 5
      src/Primitives/Test/UVSphereTest.cpp
  12. 23
      src/Primitives/UVSphere.cpp
  13. 29
      src/Primitives/UVSphere.h

4
src/Primitives/CMakeLists.txt

@ -32,7 +32,9 @@ set(MagnumPrimitives_SRCS
Line.cpp Line.cpp
Plane.cpp Plane.cpp
Square.cpp Square.cpp
UVSphere.cpp) UVSphere.cpp
Implementation/Spheroid.cpp)
set(MagnumPrimitives_HEADERS set(MagnumPrimitives_HEADERS
Capsule.h Capsule.h

138
src/Primitives/Capsule.cpp

@ -24,147 +24,43 @@
#include "Capsule.h" #include "Capsule.h"
#include "Math/Functions.h" #include "Math/Angle.h"
#include "Math/Vector3.h" #include "Primitives/Implementation/Spheroid.h"
namespace Magnum { namespace Primitives { namespace Magnum { namespace Primitives {
Capsule::Capsule(UnsignedInt hemisphereRings, UnsignedInt cylinderRings, UnsignedInt segments, Float length, TextureCoords textureCoords): MeshData3D(Mesh::Primitive::Triangles, new std::vector<UnsignedInt>, {new std::vector<Vector3>()}, {new std::vector<Vector3>()}, textureCoords == TextureCoords::Generate ? std::vector<std::vector<Vector2>*>{new std::vector<Vector2>()} : std::vector<std::vector<Vector2>*>()), segments(segments), textureCoords(textureCoords) { 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", ); 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 height = 2.0f+length;
Float hemisphereTextureCoordsVIncrement = 1.0f/(hemisphereRings*height); Float hemisphereTextureCoordsVIncrement = 1.0f/(hemisphereRings*height);
Rad hemisphereRingAngleIncrement(Constants::pi()/(2*hemisphereRings)); Rad hemisphereRingAngleIncrement(Constants::pi()/(2*hemisphereRings));
/* Bottom cap vertex */ /* Bottom cap vertex */
capVertex(-height/2, -1.0f, 0.0f); capsule.capVertex(-height/2, -1.0f, 0.0f);
/* Rings of bottom hemisphere */ /* 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 */ /* 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 */ /* 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 */ /* Top cap vertex */
capVertex(height/2, 1.0f, 1.0f); capsule.capVertex(height/2, 1.0f, 1.0f);
/* Faces */ /* Faces */
bottomFaceRing(); capsule.bottomFaceRing();
faceRings(hemisphereRings*2-2+cylinderRings); capsule.faceRings(hemisphereRings*2-2+cylinderRings);
topFaceRing(); capsule.topFaceRing();
}
Capsule::Capsule(UnsignedInt segments, TextureCoords textureCoords): MeshData3D(Mesh::Primitive::Triangles, new std::vector<UnsignedInt>, {new std::vector<Vector3>()}, {new std::vector<Vector3>()}, textureCoords == TextureCoords::Generate ? std::vector<std::vector<Vector2>*>{new std::vector<Vector2>()} : std::vector<std::vector<Vector2>*>()), 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);
/* Top vertex */ return std::move(capsule);
indices()->push_back(normals(0)->size()-1);
}
} }
}} }}

38
src/Primitives/Capsule.h

@ -28,56 +28,40 @@
* @brief Class Magnum::Primitives::Capsule * @brief Class Magnum::Primitives::Capsule
*/ */
#include "Trade/MeshData3D.h"
#include "Primitives/magnumPrimitivesVisibility.h" #include "Primitives/magnumPrimitivesVisibility.h"
#include "Trade/Trade.h"
namespace Magnum { namespace Primitives { namespace Magnum { namespace Primitives {
/** /**
@brief 3D capsule primitive @brief 3D capsule primitive
%Cylinder along Y axis with hemispheres instead of caps. Indexed @ref Mesh::Primitive "Triangles" %Cylinder of radius `1` along Y axis with hemispheres instead of caps.
with normals and optional 2D texture coordinates.
*/ */
class Capsule: public Trade::MeshData3D { class MAGNUM_PRIMITIVES_EXPORT Capsule {
friend class UVSphere;
friend class Cylinder;
public: public:
/** @brief Whether to generate texture coordinates */ /** @brief Whether to generate texture coordinates */
enum class TextureCoords { enum class TextureCoords: UnsignedByte {
Generate, /**< Generate texture coordinates */ Generate, /**< Generate texture coordinates */
DontGenerate /**< Don't generate texture coordinates */ DontGenerate /**< Don't generate texture coordinates */
}; };
/** /**
* @brief Constructor * @brief Solid capsule
* @param hemisphereRings Number of (face) rings for each hemisphere. * @param hemisphereRings Number of (face) rings for each hemisphere.
* Must be larger or equal to 1. * Must be larger or equal to 1.
* @param cylinderRings Number of (face) rings for cylinder. Must be * @param cylinderRings Number of (face) rings for cylinder. Must be
* larger or equal to 1. * 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 length Length of the capsule, excluding hemispheres.
* @param textureCoords Whether to generate texture coordinates. * @param textureCoords Whether to generate texture coordinates.
* *
* If texture coordinates are generated, vertices of one segment are * Indexed @ref Mesh::Primitive "Triangles" with normals and optional
* duplicated for texture wrapping. * 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); static Trade::MeshData3D solid(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;
}; };
}} }}

2
src/Primitives/Circle.h

@ -37,7 +37,7 @@ namespace Magnum { namespace Primitives {
/** /**
@brief 2D circle primitive @brief 2D circle primitive
Circle with radius 1. Circle with radius `1`.
*/ */
class MAGNUM_PRIMITIVES_EXPORT Circle { class MAGNUM_PRIMITIVES_EXPORT Circle {
public: public:

44
src/Primitives/Cylinder.cpp

@ -24,56 +24,40 @@
#include "Cylinder.h" #include "Cylinder.h"
#include "Math/Functions.h"
#include "Math/Vector3.h" #include "Math/Vector3.h"
#include "Primitives/Implementation/Spheroid.h"
namespace Magnum { namespace Primitives { namespace Magnum { namespace Primitives {
Cylinder::Cylinder(UnsignedInt rings, UnsignedInt segments, Float length, Flags flags): Capsule(segments, flags & Flag::GenerateTextureCoords ? TextureCoords::Generate : TextureCoords::DontGenerate) { Trade::MeshData3D Cylinder::solid(UnsignedInt rings, UnsignedInt segments, Float length, Cylinder::Flags flags) {
CORRADE_ASSERT(rings >= 1 && segments >= 3, "Cylinder must have at least one ring and three segments", ); 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 y = length*0.5f;
Float textureCoordsV = flags & Flag::CapEnds ? 1.0f/(length+2.0f) : 0.0f; Float textureCoordsV = flags & Flag::CapEnds ? 1.0f/(length+2.0f) : 0.0f;
/* Bottom cap */ /* Bottom cap */
if(flags & Flag::CapEnds) { if(flags & Flag::CapEnds) {
capVertex(-y, -1.0f, 0.0f); cylinder.capVertex(-y, -1.0f, 0.0f);
capVertexRing(-y, textureCoordsV, Vector3::yAxis(-1.0f)); cylinder.capVertexRing(-y, textureCoordsV, Vector3::yAxis(-1.0f));
} }
/* Vertex rings */ /* 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 */ /* Top cap */
if(flags & Flag::CapEnds) { if(flags & Flag::CapEnds) {
capVertexRing(y, 1.0f - textureCoordsV, Vector3::yAxis(1.0f)); cylinder.capVertexRing(y, 1.0f - textureCoordsV, Vector3::yAxis(1.0f));
capVertex(y, 1.0f, 1.0f); cylinder.capVertex(y, 1.0f, 1.0f);
} }
/* Faces */ /* Faces */
if(flags & Flag::CapEnds) bottomFaceRing(); if(flags & Flag::CapEnds) cylinder.bottomFaceRing();
faceRings(rings, flags & Flag::CapEnds ? 1 : 0); cylinder.faceRings(rings, flags & Flag::CapEnds ? 1 : 0);
if(flags & Flag::CapEnds) topFaceRing(); if(flags & Flag::CapEnds) cylinder.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(textureCoords == TextureCoords::Generate) return std::move(cylinder);
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});
}
} }
}} }}

21
src/Primitives/Cylinder.h

@ -30,19 +30,17 @@
#include <Containers/EnumSet.h> #include <Containers/EnumSet.h>
#include "Primitives/Capsule.h"
#include "Primitives/magnumPrimitivesVisibility.h" #include "Primitives/magnumPrimitivesVisibility.h"
#include "Trade/Trade.h"
namespace Magnum { namespace Primitives { namespace Magnum { namespace Primitives {
/** /**
@brief 3D cylinder primitive @brief 3D cylinder primitive
Indexed @ref Mesh::Primitive "Triangles" with normals, optional 2D texture %Cylinder along Y axis of radius `1`.
coordinates and optional capped ends.
*/ */
class Cylinder: public Capsule { class MAGNUM_PRIMITIVES_EXPORT Cylinder {
public: public:
/** /**
* @brief %Flags * @brief %Flags
@ -58,7 +56,7 @@ class Cylinder: public Capsule {
typedef Corrade::Containers::EnumSet<Flag, int> Flags; typedef Corrade::Containers::EnumSet<Flag, int> Flags;
/** /**
* @brief Constructor * @brief Solid cylinder
* @param rings Number of (face) rings. Must be larger or * @param rings Number of (face) rings. Must be larger or
* equal to 1. * equal to 1.
* @param segments Number of (face) segments. Must be larger or * @param segments Number of (face) segments. Must be larger or
@ -66,13 +64,12 @@ class Cylinder: public Capsule {
* @param length Cylinder length * @param length Cylinder length
* @param flags Flags * @param flags Flags
* *
* If texture coordinates are generated, vertices of one segment are * Indexed @ref Mesh::Primitive "Triangles" with normals, optional 2D
* duplicated for texture wrapping. * 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()); static Trade::MeshData3D solid(UnsignedInt rings, UnsignedInt segments, Float length, Flags flags = Flags());
private:
void capVertexRing(Float y, Float textureCoordsV, const Vector3& normal);
}; };
CORRADE_ENUMSET_OPERATORS(Cylinder::Flags) CORRADE_ENUMSET_OPERATORS(Cylinder::Flags)

162
src/Primitives/Implementation/Spheroid.cpp

@ -0,0 +1,162 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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<UnsignedInt>, {new std::vector<Vector3>()}, {new std::vector<Vector3>()}, textureCoords == TextureCoords::Generate ? std::vector<std::vector<Vector2>*>{new std::vector<Vector2>()} : std::vector<std::vector<Vector2>*>()), 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});
}
}
}}}

54
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š <mosra@centrum.cz>
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

5
src/Primitives/Test/CapsuleTest.cpp

@ -29,6 +29,7 @@
#include <TestSuite/Compare/Container.h> #include <TestSuite/Compare/Container.h>
#include "Math/Vector3.h" #include "Math/Vector3.h"
#include "Trade/MeshData3D.h"
#include "Primitives/Capsule.h" #include "Primitives/Capsule.h"
using Corrade::TestSuite::Compare::Container; using Corrade::TestSuite::Compare::Container;
@ -49,7 +50,7 @@ CapsuleTest::CapsuleTest() {
} }
void CapsuleTest::withoutTextureCoords() { 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<Vector3>{ CORRADE_COMPARE_AS(*capsule.positions(0), (std::vector<Vector3>{
{0.0f, -1.5f, 0.0f}, {0.0f, -1.5f, 0.0f},
@ -114,7 +115,7 @@ void CapsuleTest::withoutTextureCoords() {
} }
void CapsuleTest::withTextureCoords() { 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<Vector3>{ CORRADE_COMPARE_AS(*capsule.positions(0), (std::vector<Vector3>{
{0.0f, -1.5f, 0.0f}, {0.0f, -1.5f, 0.0f},

5
src/Primitives/Test/CylinderTest.cpp

@ -27,6 +27,7 @@
#include "Math/Vector3.h" #include "Math/Vector3.h"
#include "Primitives/Cylinder.h" #include "Primitives/Cylinder.h"
#include "Trade/MeshData3D.h"
using Corrade::TestSuite::Compare::Container; using Corrade::TestSuite::Compare::Container;
@ -46,7 +47,7 @@ CylinderTest::CylinderTest() {
} }
void CylinderTest::withoutAnything() { 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<Vector3>{ CORRADE_COMPARE_AS(*cylinder.positions(0), (std::vector<Vector3>{
{0.0f, -1.5f, 1.0f}, {0.0f, -1.5f, 1.0f},
@ -83,7 +84,7 @@ void CylinderTest::withoutAnything() {
} }
void CylinderTest::withTextureCoordsAndCaps() { 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<Vector3>{ CORRADE_COMPARE_AS(*cylinder.positions(0), (std::vector<Vector3>{
{0.0f, -1.5f, 0.0f}, {0.0f, -1.5f, 0.0f},

5
src/Primitives/Test/UVSphereTest.cpp

@ -27,6 +27,7 @@
#include "Math/Vector3.h" #include "Math/Vector3.h"
#include "Primitives/UVSphere.h" #include "Primitives/UVSphere.h"
#include "Trade/MeshData3D.h"
using Corrade::TestSuite::Compare::Container; using Corrade::TestSuite::Compare::Container;
@ -46,7 +47,7 @@ UVSphereTest::UVSphereTest() {
} }
void UVSphereTest::withoutTextureCoords() { void UVSphereTest::withoutTextureCoords() {
UVSphere sphere(3, 3); Trade::MeshData3D sphere = UVSphere::solid(3, 3);
CORRADE_COMPARE_AS(*sphere.positions(0), (std::vector<Vector3>{ CORRADE_COMPARE_AS(*sphere.positions(0), (std::vector<Vector3>{
{0.0f, -1.0f, 0.0f}, {0.0f, -1.0f, 0.0f},
@ -84,7 +85,7 @@ void UVSphereTest::withoutTextureCoords() {
} }
void UVSphereTest::withTextureCoords() { 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<Vector3>{ CORRADE_COMPARE_AS(*sphere.positions(0), (std::vector<Vector3>{
{0.0f, -1.0f, 0.0f}, {0.0f, -1.0f, 0.0f},

23
src/Primitives/UVSphere.cpp

@ -25,28 +25,35 @@
#include "UVSphere.h" #include "UVSphere.h"
#include "Math/Angle.h" #include "Math/Angle.h"
#include "Implementation/Spheroid.h"
namespace Magnum { namespace Primitives { namespace Magnum { namespace Primitives {
UVSphere::UVSphere(UnsignedInt rings, UnsignedInt segments, TextureCoords textureCoords): Capsule(segments, textureCoords) { 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", ); 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; Float textureCoordsVIncrement = 1.0f/rings;
Rad ringAngleIncrement(Constants::pi()/rings); Rad ringAngleIncrement(Constants::pi()/rings);
/* Bottom cap vertex */ /* Bottom cap vertex */
capVertex(-1.0f, -1.0f, 0.0f); sphere.capVertex(-1.0f, -1.0f, 0.0f);
/* Vertex rings */ /* 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 */ /* Top cap vertex */
capVertex(1.0f, 1.0f, 1.0f); sphere.capVertex(1.0f, 1.0f, 1.0f);
/* Faces */ /* Faces */
bottomFaceRing(); sphere.bottomFaceRing();
faceRings(rings-2); sphere.faceRings(rings-2);
topFaceRing(); sphere.topFaceRing();
return std::move(sphere);
} }
}} }}

29
src/Primitives/UVSphere.h

@ -28,30 +28,37 @@
* @brief Class Magnum::Primitives::UVSphere * @brief Class Magnum::Primitives::UVSphere
*/ */
#include "Primitives/Capsule.h"
#include "Primitives/magnumPrimitivesVisibility.h" #include "Primitives/magnumPrimitivesVisibility.h"
#include "Trade/Trade.h"
namespace Magnum { namespace Primitives { namespace Magnum { namespace Primitives {
/** /**
@brief 3D UV sphere primitive @brief 3D UV sphere primitive
Indexed @ref Mesh::Primitive "Triangles" with normals and optional 2D texture Sphere with radius `1`.
coordinates.
*/ */
class UVSphere: public Capsule { class MAGNUM_PRIMITIVES_EXPORT UVSphere {
public: public:
/** @brief Whether to generate texture coordinates */
enum class TextureCoords: UnsignedByte {
Generate, /**< Generate texture coordinates */
DontGenerate /**< Don't generate texture coordinates */
};
/** /**
* @brief Constructor * @brief Solid UV sphere
* @param rings Number of (face) rings. Must be larger or equal to 2. * @param rings Number of (face) rings. Must be larger or equal
* @param segments Number of (face) segments. Must be larger or equal to 3. * to 2.
* @param segments Number of (face) segments. Must be larger or
* equal to 3.
* @param textureCoords Whether to generate texture coordinates. * @param textureCoords Whether to generate texture coordinates.
* *
* If texture coordinates are generated, vertices of one segment are * Indexed @ref Mesh::Primitive "Triangles" with normals and optional
* duplicated for texture wrapping. * 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);
}; };
}} }}

Loading…
Cancel
Save