Browse Source

Primitives: implement tangent generation in spheroid primitives.

pull/430/head
Vladimír Vondruš 6 years ago
parent
commit
a099a57464
  1. 6
      doc/changelog.dox
  2. 16
      src/Magnum/Primitives/Capsule.h
  3. 2
      src/Magnum/Primitives/Cone.cpp
  4. 19
      src/Magnum/Primitives/Cone.h
  5. 2
      src/Magnum/Primitives/Cylinder.cpp
  6. 17
      src/Magnum/Primitives/Cylinder.h
  7. 72
      src/Magnum/Primitives/Implementation/Spheroid.cpp
  8. 11
      src/Magnum/Primitives/Implementation/Spheroid.h
  9. 142
      src/Magnum/Primitives/Test/CapsuleTest.cpp
  10. 196
      src/Magnum/Primitives/Test/ConeTest.cpp
  11. 217
      src/Magnum/Primitives/Test/CylinderTest.cpp
  12. 97
      src/Magnum/Primitives/Test/UVSphereTest.cpp
  13. 17
      src/Magnum/Primitives/UVSphere.h

6
doc/changelog.dox

@ -192,6 +192,12 @@ See also:
bootstrap project for using Magnum together with gtkmm (see
[mosra/magnum-bootstrap#24](https://github.com/mosra/magnum-bootstrap/pull/24))
@subsubsection changelog-latest-new-primitives Primitives library
- @ref Primitives::capsule3DSolid(), @ref Primitives::coneSolid(),
@ref Primitives::cylinderSolid() and @ref Primitives::uvSphereSolid() can
now have tangents as well
@subsubsection changelog-latest-new-scenegraph SceneGraph library
- All 2D transformation implementations that support rotation now have a

16
src/Magnum/Primitives/Capsule.h

@ -63,7 +63,15 @@ MAGNUM_PRIMITIVES_EXPORT Trade::MeshData capsule2DWireframe(UnsignedInt hemisphe
@see @ref CapsuleFlags, @ref capsule3DSolid()
*/
enum class CapsuleFlag: UnsignedByte {
TextureCoordinates = 1 << 0 /**< Generate texture coordinates */
TextureCoordinates = 1 << 0, /**< Generate texture coordinates */
/**
* Generate four-component tangents. The last component can be used to
* reconstruct a bitangent as described in the documentation of
* @ref Trade::MeshAttribute::Tangent.
* @m_since_latest
*/
Tangents = 1 << 1
};
/**
@ -91,9 +99,9 @@ CORRADE_ENUMSET_OPERATORS(CapsuleFlags)
Cylinder of radius @cpp 1.0f @ce along Y axis with hemispheres instead of caps.
@ref MeshPrimitive::Triangles with @ref MeshIndexType::UnsignedInt indices,
interleaved @ref VertexFormat::Vector3 positions, @ref VertexFormat::Vector3
normals and optional @ref VertexFormat::Vector2 texture coordinates. If texture
coordinates are generated, vertices of one segment are duplicated for texture
wrapping.
normals, optional @ref VertexFormat::Vector4 tangents and optional
@ref VertexFormat::Vector2 texture coordinates. If texture coordinates are
generated, vertices of one segment are duplicated for texture wrapping.
@image html primitives-capsule3dsolid.png width=256px

2
src/Magnum/Primitives/Cone.cpp

@ -54,7 +54,7 @@ Trade::MeshData coneSolid(const UnsignedInt rings, const UnsignedInt segments, c
/* Faces. Account for the extra vertices for caps and texture coords. */
if(flags & ConeFlag::CapEnd) cone.bottomFaceRing();
if(flags >= (ConeFlag::CapEnd|ConeFlag::TextureCoordinates))
if(flags & ConeFlag::CapEnd && flags & (ConeFlag::Tangents|ConeFlag::TextureCoordinates))
cone.faceRings(rings, 2 + segments);
else if(flags & ConeFlag::CapEnd)
cone.faceRings(rings, 1 + segments);

19
src/Magnum/Primitives/Cone.h

@ -59,7 +59,15 @@ enum class ConeFlag: UnsignedByte {
GenerateTextureCoords CORRADE_DEPRECATED_ENUM("use TextureCoordinates instead") = TextureCoordinates,
#endif
CapEnd = 1 << 1 /**< Cap end */
/**
* Generate four-component tangents. The last component can be used to
* reconstruct a bitangent as described in the documentation of
* @ref Trade::MeshAttribute::Tangent.
* @m_since_latest
*/
Tangents = 1 << 1,
CapEnd = 1 << 2 /**< Cap end */
};
/**
@ -82,10 +90,11 @@ CORRADE_ENUMSET_OPERATORS(ConeFlags)
Cone along Y axis of radius @cpp 1.0f @ce. @ref MeshPrimitive::Triangles with
@ref MeshIndexType::UnsignedInt indices, interleaved @ref VertexFormat::Vector3
positions, @ref VertexFormat::Vector3 normals and optional
@ref VertexFormat::Vector2 texture coordinates. Note that in order to have
properly smooth normals over the whole area, the tip consists of
@cpp segments*2 @ce vertices instead of just one.
positions, @ref VertexFormat::Vector3 normals, optional
@ref VertexFormat::Vector4 tangents and optional @ref VertexFormat::Vector2
texture coordinates. Note that in order to have properly smooth normals over
the whole area, the tip consists of @cpp segments*2 @ce vertices instead of
just one.
@image html primitives-conesolid.png width=256px

2
src/Magnum/Primitives/Cylinder.cpp

@ -60,7 +60,7 @@ Trade::MeshData cylinderSolid(const UnsignedInt rings, const UnsignedInt segment
/* Faces. Account for the extra vertices for caps and texture coords. */
if(flags & CylinderFlag::CapEnds) cylinder.bottomFaceRing();
if(flags >= (CylinderFlag::CapEnds|CylinderFlag::TextureCoordinates))
if(flags & CylinderFlag::CapEnds && flags & (CylinderFlag::TextureCoordinates|CylinderFlag::Tangents))
cylinder.faceRings(rings, 2 + segments);
else if(flags & CylinderFlag::CapEnds)
cylinder.faceRings(rings, 1 + segments);

17
src/Magnum/Primitives/Cylinder.h

@ -58,7 +58,15 @@ enum class CylinderFlag: UnsignedByte {
GenerateTextureCoords CORRADE_DEPRECATED_ENUM("use TextureCoordinates instead") = TextureCoordinates,
#endif
CapEnds = 1 << 1 /**< Cap ends */
/**
* Generate four-component tangents. The last component can be used to
* reconstruct a bitangent as described in the documentation of
* @ref Trade::MeshAttribute::Tangent.
* @m_since_latest
*/
Tangents = 1 << 1,
CapEnds = 1 << 2 /**< Cap ends */
};
/**
@ -82,9 +90,10 @@ CORRADE_ENUMSET_OPERATORS(CylinderFlags)
Cylinder along Y axis of radius @cpp 1.0f @ce. @ref MeshPrimitive::Triangles
with @ref MeshIndexType::UnsignedInt indices, interleaved
@ref VertexFormat::Vector3 positions, @ref VertexFormat::Vector3 normals,
optional @ref VertexFormat::Vector2 texture coordinates and optional capped
ends. If texture coordinates are generated, vertices of one segment are
duplicated for texture wrapping.
optional @ref VertexFormat::Vector4 tangents, optional
@ref VertexFormat::Vector2 texture coordinates and optional capped ends. If
texture coordinates are generated, vertices of one segment are duplicated for
texture wrapping.
@image html primitives-cylindersolid.png width=256px

72
src/Magnum/Primitives/Implementation/Spheroid.cpp

@ -36,6 +36,12 @@
namespace Magnum { namespace Primitives { namespace Implementation {
Spheroid::Spheroid(UnsignedInt segments, Flags flags): _segments(segments), _flags{flags}, _stride{sizeof(Vector3) + sizeof(Vector3)}, _attributeCount{2} {
if(_flags & Flag::Tangents) {
_tangentOffset = _stride;
_stride += sizeof(Vector4);
++_attributeCount;
} else _tangentOffset = ~std::size_t{};
if(_flags & Flag::TextureCoordinates) {
_textureCoordinateOffset = _stride;
_stride += sizeof(Vector2);
@ -43,14 +49,22 @@ Spheroid::Spheroid(UnsignedInt segments, Flags flags): _segments(segments), _fla
} else _textureCoordinateOffset = ~std::size_t{};
}
void Spheroid::append(const Vector3& position, const Vector3& normal, const Vector2& textureCoords) {
void Spheroid::append(const Vector3& position, const Vector3& normal) {
Containers::arrayAppend<Trade::ArrayAllocator>(_vertexData,
Containers::arrayCast<const char>(Containers::arrayView(&position, 1)));
Containers::arrayAppend<Trade::ArrayAllocator>(_vertexData,
Containers::arrayCast<const char>(Containers::arrayView(&normal, 1)));
if(_flags & Flag::Tangents) {
/** @todo make arrayGrow() a public API instead of this */
constexpr const char empty[sizeof(Vector4)]{};
Containers::arrayAppend<Trade::ArrayAllocator>(_vertexData,
Containers::arrayView(empty));
}
if(_flags & Flag::TextureCoordinates) {
/** @todo make arrayGrow() a public API instead */
constexpr const char empty[sizeof(Vector2)]{};
Containers::arrayAppend<Trade::ArrayAllocator>(_vertexData,
Containers::arrayCast<const char>(Containers::arrayView(&textureCoords, 1)));
Containers::arrayView(empty));
}
}
@ -62,12 +76,18 @@ Vector3 Spheroid::lastVertexNormal(const std::size_t offsetFromEnd) {
return Containers::arrayCast<Vector3>(_vertexData.slice<sizeof(Vector3)>(_vertexData.size() - _stride*offsetFromEnd + sizeof(Vector3)))[0];
}
Vector4& Spheroid::lastVertexTangent(const std::size_t offsetFromEnd) {
return Containers::arrayCast<Vector4>(_vertexData.slice<sizeof(Vector4)>(_vertexData.size() - _stride*offsetFromEnd + _tangentOffset))[0];
}
Vector2& Spheroid::lastVertexTextureCoords(const std::size_t offsetFromEnd) {
return Containers::arrayCast<Vector2>(_vertexData.slice<sizeof(Vector2)>(_vertexData.size() - _stride*offsetFromEnd + _textureCoordinateOffset))[0];
}
void Spheroid::capVertex(Float y, Float normalY, Float textureCoordsV) {
append({0.0f, y, 0.0f}, {0.0f, normalY, 0.0f});
if(_flags & Flag::Tangents)
lastVertexTangent(1) = {normalY > 0.0f ? 1.0f : -1.0f, 0.0f, 0.0f, 1.0f};
if(_flags & Flag::TextureCoordinates)
lastVertexTextureCoords(1) = {0.5f, textureCoordsV};
}
@ -87,15 +107,20 @@ void Spheroid::hemisphereVertexRings(UnsignedInt count, Float centerY, Rad start
append({x*segmentSinCos.first, centerY+y, z*segmentSinCos.second},
{x*segmentSinCos.first, y, z*segmentSinCos.second});
if(_flags & Flag::Tangents)
lastVertexTangent(1) = {segmentSinCos.second, 0.0f, -segmentSinCos.first, 1.0f};
if(_flags & Flag::TextureCoordinates)
lastVertexTextureCoords(1) = {j*1.0f/_segments, startTextureCoordsV + i*textureCoordsVIncrement};
}
/* Duplicate first segment in the ring for additional vertex for
texture coordinate */
if(_flags & Flag::TextureCoordinates) {
append(lastVertexPosition(_segments), lastVertexNormal(_segments),
{1.0f, startTextureCoordsV + i*textureCoordsVIncrement});
if(_flags & (Flag::TextureCoordinates|Flag::Tangents)) {
append(lastVertexPosition(_segments), lastVertexNormal(_segments));
if(_flags & Flag::Tangents)
lastVertexTangent(1) = lastVertexTangent(_segments + 1);
if(_flags & Flag::TextureCoordinates)
lastVertexTextureCoords(1) = {1.0f, startTextureCoordsV + i*textureCoordsVIncrement};
}
}
}
@ -112,14 +137,19 @@ void Spheroid::cylinderVertexRings(const UnsignedInt count, const Float startY,
append({base.x()*segmentSinCos.first, base.y(), base.x()*segmentSinCos.second},
{baseNormal.x()*segmentSinCos.first, baseNormal.y(), baseNormal.x()*segmentSinCos.second});
if(_flags & Flag::Tangents)
lastVertexTangent(1) = {segmentSinCos.second, 0.0f, -segmentSinCos.first, 1.0f};
if(_flags & Flag::TextureCoordinates)
lastVertexTextureCoords(1) = {j*1.0f/_segments, startTextureCoordsV + i*textureCoordsVIncrement};
}
/* Duplicate first segment in the ring for additional vertex for texture coordinate */
if(_flags & Flag::TextureCoordinates) {
append(lastVertexPosition(_segments), lastVertexNormal(_segments),
{1.0f, startTextureCoordsV + i*textureCoordsVIncrement});
if(_flags & (Flag::TextureCoordinates|Flag::Tangents)) {
append(lastVertexPosition(_segments), lastVertexNormal(_segments));
if(_flags & Flag::Tangents)
lastVertexTangent(1) = lastVertexTangent(_segments + 1);
if(_flags & Flag::TextureCoordinates)
lastVertexTextureCoords(1) = {1.0f, startTextureCoordsV + i*textureCoordsVIncrement};
}
base += increment;
@ -133,7 +163,7 @@ void Spheroid::bottomFaceRing() {
0u,
/* Top right vertex */
(j != _segments-1 || _flags & Flag::TextureCoordinates) ?
(j != _segments-1 || _flags & (Flag::TextureCoordinates|Flag::Tangents)) ?
j+2 : 1,
/* Top left vertex */
@ -143,12 +173,12 @@ void Spheroid::bottomFaceRing() {
}
void Spheroid::faceRings(UnsignedInt count, UnsignedInt offset) {
const UnsignedInt vertexSegments = _segments + (_flags & Flag::TextureCoordinates ? 1 : 0);
const UnsignedInt vertexSegments = _segments + (_flags & (Flag::TextureCoordinates|Flag::Tangents) ? 1 : 0);
for(UnsignedInt i = 0; i != count; ++i) {
for(UnsignedInt j = 0; j != _segments; ++j) {
const UnsignedInt bottomLeft = i*vertexSegments+j+offset;
const UnsignedInt bottomRight = ((j != _segments-1 || _flags & Flag::TextureCoordinates) ?
const UnsignedInt bottomRight = ((j != _segments-1 || _flags & (Flag::TextureCoordinates|Flag::Tangents)) ?
i*vertexSegments+j+1+offset : i*_segments+offset);
const UnsignedInt topLeft = bottomLeft+vertexSegments;
const UnsignedInt topRight = bottomRight+vertexSegments;
@ -166,7 +196,7 @@ void Spheroid::faceRings(UnsignedInt count, UnsignedInt offset) {
}
void Spheroid::topFaceRing() {
const UnsignedInt vertexSegments = _segments + (_flags & Flag::TextureCoordinates ? 1 : 0);
const UnsignedInt vertexSegments = _segments + (_flags & (Flag::TextureCoordinates|Flag::Tangents) ? 1 : 0);
const UnsignedInt vertexCount = _vertexData.size()/_stride;
@ -176,7 +206,7 @@ void Spheroid::topFaceRing() {
vertexCount - vertexSegments + j - 1,
/* Bottom right vertex */
(j != _segments-1 || _flags & Flag::TextureCoordinates) ?
(j != _segments-1 || _flags & (Flag::TextureCoordinates|Flag::Tangents)) ?
vertexCount - vertexSegments + j : vertexCount - _segments - 1,
/* Top vertex */
@ -193,14 +223,20 @@ void Spheroid::capVertexRing(Float y, Float textureCoordsV, const Vector3& norma
const std::pair<Float, Float> segmentSinCos = Math::sincos(segmentAngle);
append({segmentSinCos.first, y, segmentSinCos.second}, normal);
if(_flags & Flag::Tangents)
lastVertexTangent(1) = {segmentSinCos.second, 0.0f, -segmentSinCos.first, 1.0f};
if(_flags & Flag::TextureCoordinates)
lastVertexTextureCoords(1) = {i*1.0f/_segments, textureCoordsV};
}
/* Duplicate first segment in the ring for additional vertex for texture
coordinate */
if(_flags & Flag::TextureCoordinates) {
append(lastVertexPosition(_segments), normal, {1.0f, textureCoordsV});
if(_flags & (Flag::TextureCoordinates|Flag::Tangents)) {
append(lastVertexPosition(_segments), normal);
if(_flags & Flag::Tangents)
lastVertexTangent(1) = lastVertexTangent(_segments + 1);
if(_flags & Flag::TextureCoordinates)
lastVertexTextureCoords(1) = {1.0f, textureCoordsV};
}
}
@ -220,6 +256,12 @@ Trade::MeshData Spheroid::finalize() {
_vertexData.data() + sizeof(Vector3),
_vertexData.size()/_stride, std::ptrdiff_t(_stride))};
if(_flags & Flag::Tangents)
attributes[attributeOffset++] = Trade::MeshAttributeData{
Trade::MeshAttribute::Tangent, VertexFormat::Vector4,
Containers::stridedArrayView(_vertexData,
_vertexData.data() + _tangentOffset,
_vertexData.size()/_stride, std::ptrdiff_t(_stride))};
if(_flags & Flag::TextureCoordinates)
attributes[attributeOffset++] = Trade::MeshAttributeData{
Trade::MeshAttribute::TextureCoordinates, VertexFormat::Vector2,

11
src/Magnum/Primitives/Implementation/Spheroid.h

@ -29,7 +29,7 @@
#include <Corrade/Containers/StridedArrayView.h>
#include "Magnum/Magnum.h"
#include "Magnum/Math/Vector2.h"
#include "Magnum/Math/Vector4.h"
#include "Magnum/Trade/Trade.h"
namespace Magnum { namespace Primitives { namespace Implementation {
@ -37,7 +37,8 @@ namespace Magnum { namespace Primitives { namespace Implementation {
class Spheroid {
public:
enum class Flag: UnsignedByte {
TextureCoordinates = 1 << 0
TextureCoordinates = 1 << 0,
Tangents = 1 << 1
};
typedef Containers::EnumSet<Flag> Flags;
@ -58,15 +59,17 @@ class Spheroid {
UnsignedInt _segments;
Flags _flags;
std::size_t _stride;
std::size_t _textureCoordinateOffset;
std::size_t _textureCoordinateOffset,
_tangentOffset;
std::size_t _attributeCount;
Containers::Array<UnsignedInt> _indexData;
Containers::Array<char> _vertexData;
void append(const Vector3& position, const Vector3& normal, const Vector2& textureCoords = {});
void append(const Vector3& position, const Vector3& normal);
Vector3 lastVertexPosition(std::size_t offsetFromEnd);
Vector3 lastVertexNormal(std::size_t offsetFromEnd);
Vector4& lastVertexTangent(std::size_t offsetFromEnd);
Vector2& lastVertexTextureCoords(std::size_t offsetFromEnd);
};

142
src/Magnum/Primitives/Test/CapsuleTest.cpp

@ -26,7 +26,7 @@
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/TestSuite/Compare/Container.h>
#include "Magnum/Math/Vector3.h"
#include "Magnum/Math/Vector4.h"
#include "Magnum/Trade/MeshData.h"
#include "Magnum/Primitives/Capsule.h"
@ -37,16 +37,28 @@ struct CapsuleTest: TestSuite::Tester {
void wireframe2D();
void solid3DWithoutTextureCoords();
void solid3DWithTextureCoords();
void solid3DWithoutTextureCoordinates();
void solid3DWithTextureCoordinatesOrTangents();
void wireframe3D();
};
constexpr struct {
const char* name;
CapsuleFlags flags;
} TextureCoordinatesOrTangentsData[] {
{"texture coordinates", CapsuleFlag::TextureCoordinates},
{"tangents", CapsuleFlag::Tangents},
{"both", CapsuleFlag::TextureCoordinates|CapsuleFlag::Tangents}
};
CapsuleTest::CapsuleTest() {
addTests({&CapsuleTest::wireframe2D,
&CapsuleTest::solid3DWithoutTextureCoords,
&CapsuleTest::solid3DWithTextureCoords,
&CapsuleTest::wireframe3D});
&CapsuleTest::solid3DWithoutTextureCoordinates});
addInstancedTests({&CapsuleTest::solid3DWithTextureCoordinatesOrTangents},
Containers::arraySize(TextureCoordinatesOrTangentsData));
addTests({&CapsuleTest::wireframe3D});
}
void CapsuleTest::wireframe2D() {
@ -97,7 +109,7 @@ void CapsuleTest::wireframe2D() {
}), TestSuite::Compare::Container);
}
void CapsuleTest::solid3DWithoutTextureCoords() {
void CapsuleTest::solid3DWithoutTextureCoordinates() {
Trade::MeshData capsule = capsule3DSolid(2, 4, 3, 0.5f);
CORRADE_COMPARE(capsule.primitive(), MeshPrimitive::Triangles);
@ -184,12 +196,14 @@ void CapsuleTest::solid3DWithoutTextureCoords() {
}), TestSuite::Compare::Container);
}
void CapsuleTest::solid3DWithTextureCoords() {
Trade::MeshData capsule = capsule3DSolid(2, 2, 3, 0.5f, CapsuleFlag::TextureCoordinates);
void CapsuleTest::solid3DWithTextureCoordinatesOrTangents() {
auto&& data = TextureCoordinatesOrTangentsData[testCaseInstanceId()];
setTestCaseDescription(data.name);
Trade::MeshData capsule = capsule3DSolid(2, 2, 3, 0.5f, data.flags);
CORRADE_COMPARE(capsule.primitive(), MeshPrimitive::Triangles);
CORRADE_VERIFY(capsule.isIndexed());
CORRADE_COMPARE(capsule.attributeCount(), 3);
CORRADE_COMPARE_AS(capsule.attribute<Vector3>(Trade::MeshAttribute::Position), Containers::arrayView<Vector3>({
{0.0f, -1.5f, 0.0f},
@ -222,36 +236,84 @@ void CapsuleTest::solid3DWithTextureCoords() {
{0.0f, 1.5f, 0.0f}
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(capsule.attribute<Vector2>(Trade::MeshAttribute::TextureCoordinates), Containers::arrayView<Vector2>({
{0.5f, 0.0f},
{0.0f, 0.166667f},
{0.333333f, 0.166667f},
{0.666667f, 0.166667f},
{1.0f, 0.166667f},
{0.0f, 0.333333f},
{0.333333f, 0.333333f},
{0.666667f, 0.333333f},
{1.0f, 0.333333f},
{0.0f, 0.5f},
{0.333333f, 0.5f},
{0.666667f, 0.5f},
{1.0f, 0.5f},
{0.0f, 0.666667f},
{0.333333f, 0.666667f},
{0.666667f, 0.666667f},
{1.0f, 0.666667f},
{0.0f, 0.833333f},
{0.333333f, 0.833333f},
{0.666667f, 0.833333f},
{1.0f, 0.833333f},
{0.5f, 1.0f}
}), TestSuite::Compare::Container);
if(data.flags & CapsuleFlag::Tangents) {
CORRADE_COMPARE_AS(capsule.attribute<Vector4>(Trade::MeshAttribute::Tangent), Containers::arrayView<Vector4>({
{-1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{-0.5f, 0.0f, -0.866025f, 1.0f},
{-0.5f, 0.0f, 0.866025f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{-0.5f, 0.0f, -0.866025f, 1.0f},
{-0.5f, 0.0f, 0.866025f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{-0.5f, 0.0f, -0.866025f, 1.0f},
{-0.5f, 0.0f, 0.866025f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{-0.5f, 0.0f, -0.866025f, 1.0f},
{-0.5f, 0.0f, 0.866025f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{-0.5f, 0.0f, -0.866025f, 1.0f},
{-0.5f, 0.0f, 0.866025f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f}
}), TestSuite::Compare::Container);
} else CORRADE_VERIFY(!capsule.hasAttribute(Trade::MeshAttribute::Tangent));
if(data.flags & CapsuleFlag::TextureCoordinates) {
CORRADE_COMPARE_AS(capsule.attribute<Vector2>(Trade::MeshAttribute::TextureCoordinates), Containers::arrayView<Vector2>({
{0.5f, 0.0f},
{0.0f, 0.166667f},
{0.333333f, 0.166667f},
{0.666667f, 0.166667f},
{1.0f, 0.166667f},
{0.0f, 0.333333f},
{0.333333f, 0.333333f},
{0.666667f, 0.333333f},
{1.0f, 0.333333f},
{0.0f, 0.5f},
{0.333333f, 0.5f},
{0.666667f, 0.5f},
{1.0f, 0.5f},
{0.0f, 0.666667f},
{0.333333f, 0.666667f},
{0.666667f, 0.666667f},
{1.0f, 0.666667f},
{0.0f, 0.833333f},
{0.333333f, 0.833333f},
{0.666667f, 0.833333f},
{1.0f, 0.833333f},
{0.5f, 1.0f}
}), TestSuite::Compare::Container);
} else CORRADE_VERIFY(!capsule.hasAttribute(Trade::MeshAttribute::TextureCoordinates));
if(data.flags & CapsuleFlag::Tangents) {
auto tangents = capsule.attribute<Vector4>(Trade::MeshAttribute::Tangent);
auto normals = capsule.attribute<Vector3>(Trade::MeshAttribute::Normal);
for(std::size_t i = 0; i != tangents.size(); ++i) {
CORRADE_ITERATION(i);
CORRADE_ITERATION(tangents[i]);
CORRADE_ITERATION(normals[i]);
CORRADE_VERIFY(tangents[i].xyz().isNormalized());
CORRADE_VERIFY(normals[i].isNormalized());
CORRADE_COMPARE(Math::dot(tangents[i].xyz(), normals[i]), 0.0f);
}
}
CORRADE_COMPARE_AS(capsule.indices<UnsignedInt>(), Containers::arrayView<UnsignedInt>({
0, 2, 1, 0, 3, 2, 0, 4, 3,

196
src/Magnum/Primitives/Test/ConeTest.cpp

@ -26,7 +26,7 @@
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/TestSuite/Compare/Container.h>
#include "Magnum/Math/Vector3.h"
#include "Magnum/Math/Vector4.h"
#include "Magnum/Primitives/Cone.h"
#include "Magnum/Trade/MeshData.h"
@ -37,17 +37,30 @@ struct ConeTest: TestSuite::Tester {
void solidWithoutAnything();
void solidWithCaps();
void solidWithTextureCoords();
void solidWithTextureCoordsAndCaps();
void solidWithTextureCoordinatesOrTangents();
void solidWithTextureCoordinatesOrTangentsAndCaps();
void wireframe();
};
constexpr struct {
const char* name;
ConeFlags flags;
} TextureCoordinatesOrTangentsData[] {
{"texture coordinates", ConeFlag::TextureCoordinates},
{"tangents", ConeFlag::Tangents},
{"both", ConeFlag::TextureCoordinates|ConeFlag::Tangents}
};
ConeTest::ConeTest() {
addTests({&ConeTest::solidWithoutAnything,
&ConeTest::solidWithCaps,
&ConeTest::solidWithTextureCoords,
&ConeTest::solidWithTextureCoordsAndCaps,
&ConeTest::wireframe});
&ConeTest::solidWithCaps});
addInstancedTests({
&ConeTest::solidWithTextureCoordinatesOrTangents,
&ConeTest::solidWithTextureCoordinatesOrTangentsAndCaps},
Containers::arraySize(TextureCoordinatesOrTangentsData));
addTests({&ConeTest::wireframe});
}
void ConeTest::solidWithoutAnything() {
@ -149,12 +162,14 @@ void ConeTest::solidWithCaps() {
}), TestSuite::Compare::Container);
}
void ConeTest::solidWithTextureCoords() {
Trade::MeshData cone = coneSolid(2, 3, 1.0f, ConeFlag::TextureCoordinates);
void ConeTest::solidWithTextureCoordinatesOrTangents() {
auto&& data = TextureCoordinatesOrTangentsData[testCaseInstanceId()];
setTestCaseDescription(data.name);
Trade::MeshData cone = coneSolid(2, 3, 1.0f, data.flags);
CORRADE_COMPARE(cone.primitive(), MeshPrimitive::Triangles);
CORRADE_VERIFY(cone.isIndexed());
CORRADE_COMPARE(cone.attributeCount(), 3);
/* Bottom ring duplicated because it has different normals, first vertex of
each ring duplicated because it has different texture coordinates */
@ -175,6 +190,25 @@ void ConeTest::solidWithTextureCoords() {
{0.0f, 1.0f, 0.0f} /* 11 */
}), TestSuite::Compare::Container);
if(data.flags & ConeFlag::Tangents) {
CORRADE_COMPARE_AS(cone.attribute<Vector4>(Trade::MeshAttribute::Tangent), Containers::arrayView<Vector4>({
{1.0f, 0.0f, 0.0f, 1.0f}, /* 0 */
{-0.5f, 0.0f, -0.866025f, 1.0f}, /* 1 */
{-0.5f, 0.0f, 0.866025f, 1.0f}, /* 2 */
{1.0f, 0.0f, 0.0f, 1.0f}, /* 3 */
{1.0f, 0.0f, 0.0f, 1.0f}, /* 4 */
{-0.5f, 0.0f, -0.866025f, 1.0f}, /* 5 */
{-0.5f, 0.0f, 0.866025f, 1.0f}, /* 6 */
{1.0f, 0.0f, 0.0f, 1.0f}, /* 7 */
{1.0f, 0.0f, 0.0f, 1.0f}, /* 8 */
{-0.5f, 0.0f, -0.866025f, 1.0f}, /* 9 */
{-0.5f, 0.0f, 0.866025f, 1.0f}, /* 10 */
{1.0f, 0.0f, 0.0f, 1.0f} /* 11 */
}), TestSuite::Compare::Container);
} else CORRADE_VERIFY(!cone.hasAttribute(Trade::MeshAttribute::Tangent));
CORRADE_COMPARE_AS(cone.attribute<Vector3>(Trade::MeshAttribute::Normal), Containers::arrayView<Vector3>({
{0.0f, 0.447214f, 0.894427f}, /* 0 */
{0.774597f, 0.447214f, -0.447214f}, /* 1 */
@ -192,22 +226,37 @@ void ConeTest::solidWithTextureCoords() {
{0.0f, 0.447214f, 0.894427f} /* 11 */
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(cone.attribute<Vector2>(Trade::MeshAttribute::TextureCoordinates), Containers::arrayView<Vector2>({
{0.0f, 0.0f}, /* 0 */
{0.333333f, 0.0f}, /* 1 */
{0.666667f, 0.0f}, /* 2 */
{1.0f, 0.0f}, /* 3 */
{0.0f, 0.5f}, /* 4 */
{0.333333f, 0.5f}, /* 5 */
{0.666667f, 0.5f}, /* 6 */
{1.0f, 0.5f}, /* 7 */
{0.0f, 1.0f}, /* 8 */
{0.333333f, 1.0f}, /* 9 */
{0.666667f, 1.0f}, /* 10 */
{1.0f, 1.0f}, /* 11 */
}), TestSuite::Compare::Container);
if(data.flags & ConeFlag::TextureCoordinates) {
CORRADE_COMPARE_AS(cone.attribute<Vector2>(Trade::MeshAttribute::TextureCoordinates), Containers::arrayView<Vector2>({
{0.0f, 0.0f}, /* 0 */
{0.333333f, 0.0f}, /* 1 */
{0.666667f, 0.0f}, /* 2 */
{1.0f, 0.0f}, /* 3 */
{0.0f, 0.5f}, /* 4 */
{0.333333f, 0.5f}, /* 5 */
{0.666667f, 0.5f}, /* 6 */
{1.0f, 0.5f}, /* 7 */
{0.0f, 1.0f}, /* 8 */
{0.333333f, 1.0f}, /* 9 */
{0.666667f, 1.0f}, /* 10 */
{1.0f, 1.0f}, /* 11 */
}), TestSuite::Compare::Container);
} else CORRADE_VERIFY(!cone.hasAttribute(Trade::MeshAttribute::TextureCoordinates));
if(data.flags & ConeFlag::Tangents) {
auto tangents = cone.attribute<Vector4>(Trade::MeshAttribute::Tangent);
auto normals = cone.attribute<Vector3>(Trade::MeshAttribute::Normal);
for(std::size_t i = 0; i != tangents.size(); ++i) {
CORRADE_ITERATION(i);
CORRADE_ITERATION(tangents[i]);
CORRADE_ITERATION(normals[i]);
CORRADE_VERIFY(tangents[i].xyz().isNormalized());
CORRADE_VERIFY(normals[i].isNormalized());
CORRADE_COMPARE(Math::dot(tangents[i].xyz(), normals[i]), 0.0f);
}
}
/* Each ring has an extra vertex for texture coords */
CORRADE_COMPARE_AS(cone.indices<UnsignedInt>(), Containers::arrayView<UnsignedInt>({
@ -216,12 +265,14 @@ void ConeTest::solidWithTextureCoords() {
}), TestSuite::Compare::Container);
}
void ConeTest::solidWithTextureCoordsAndCaps() {
Trade::MeshData cone = coneSolid(2, 3, 1.0f, ConeFlag::TextureCoordinates|ConeFlag::CapEnd);
void ConeTest::solidWithTextureCoordinatesOrTangentsAndCaps() {
auto&& data = TextureCoordinatesOrTangentsData[testCaseInstanceId()];
setTestCaseDescription(data.name);
Trade::MeshData cone = coneSolid(2, 3, 1.0f, data.flags|ConeFlag::CapEnd);
CORRADE_COMPARE(cone.primitive(), MeshPrimitive::Triangles);
CORRADE_VERIFY(cone.isIndexed());
CORRADE_COMPARE(cone.attributeCount(), 3);
/* Bottom ring duplicated because it has different normals, first vertex of
each ring duplicated because it has different texture coordinates */
@ -249,6 +300,32 @@ void ConeTest::solidWithTextureCoordsAndCaps() {
{0.0f, 1.0f, 0.0f} /* 16 */
}), TestSuite::Compare::Container);
if(data.flags & ConeFlag::Tangents) {
CORRADE_COMPARE_AS(cone.attribute<Vector4>(Trade::MeshAttribute::Tangent), Containers::arrayView<Vector4>({
{-1.0f, 0.0f, 0.0f, 1.0f}, /* 0 */
{1.0f, 0.0f, 0.0f, 1.0f}, /* 1 */
{-0.5f, 0.0f, -0.866025f, 1.0f}, /* 2 */
{-0.5f, 0.0f, 0.866025f, 1.0f}, /* 3 */
{1.0f, 0.0f, 0.0f, 1.0f}, /* 4 */
{1.0f, 0.0f, 0.0f, 1.0f}, /* 5 */
{-0.5f, 0.0f, -0.866025f, 1.0f}, /* 6 */
{-0.5f, 0.0f, 0.866025f, 1.0f}, /* 7 */
{1.0f, 0.0f, 0.0f, 1.0f}, /* 8 */
{1.0f, 0.0f, 0.0f, 1.0f}, /* 9 */
{-0.5f, 0.0f, -0.866025f, 1.0f}, /* 10 */
{-0.5f, 0.0f, 0.866025f, 1.0f}, /* 11 */
{1.0f, 0.0f, 0.0f, 1.0f}, /* 12 */
{1.0f, 0.0f, 0.0f, 1.0f}, /* 13 */
{-0.5f, 0.0f, -0.866025f, 1.0f}, /* 14 */
{-0.5f, 0.0f, 0.866025f, 1.0f}, /* 15 */
{1.0f, 0.0f, 0.0f, 1.0f} /* 16 */
}), TestSuite::Compare::Container);
} else CORRADE_VERIFY(!cone.hasAttribute(Trade::MeshAttribute::Tangent));
CORRADE_COMPARE_AS(cone.attribute<Vector3>(Trade::MeshAttribute::Normal), Containers::arrayView<Vector3>({
{0.0f, -1.0f, 0.0f}, /* 0 */
@ -273,29 +350,44 @@ void ConeTest::solidWithTextureCoordsAndCaps() {
{0.0f, 0.447214f, 0.894427f} /* 16 */
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(cone.attribute<Vector2>(Trade::MeshAttribute::TextureCoordinates), Containers::arrayView<Vector2>({
{0.5f, 0.0f}, /* 0 */
{0.0f, 0.333333f}, /* 1 */
{0.333333f, 0.333333f}, /* 2 */
{0.666667f, 0.333333f}, /* 3 */
{1.0f, 0.333333f}, /* 4 */
{0.0f, 0.333333f}, /* 5 */
{0.333333f, 0.333333f}, /* 6 */
{0.666667f, 0.333333f}, /* 7 */
{1.0f, 0.333333f}, /* 8 */
{0.0f, 0.666667f}, /* 9 */
{0.333333f, 0.666667f}, /* 10 */
{0.666667f, 0.666667f}, /* 11 */
{1.0f, 0.666667f}, /* 12 */
{0.0f, 1.0f}, /* 13 */
{0.333333f, 1.0f}, /* 14 */
{0.666667f, 1.0f}, /* 15 */
{1.0f, 1.0f}, /* 16 */
}), TestSuite::Compare::Container);
if(data.flags & ConeFlag::TextureCoordinates) {
CORRADE_COMPARE_AS(cone.attribute<Vector2>(Trade::MeshAttribute::TextureCoordinates), Containers::arrayView<Vector2>({
{0.5f, 0.0f}, /* 0 */
{0.0f, 0.333333f}, /* 1 */
{0.333333f, 0.333333f}, /* 2 */
{0.666667f, 0.333333f}, /* 3 */
{1.0f, 0.333333f}, /* 4 */
{0.0f, 0.333333f}, /* 5 */
{0.333333f, 0.333333f}, /* 6 */
{0.666667f, 0.333333f}, /* 7 */
{1.0f, 0.333333f}, /* 8 */
{0.0f, 0.666667f}, /* 9 */
{0.333333f, 0.666667f}, /* 10 */
{0.666667f, 0.666667f}, /* 11 */
{1.0f, 0.666667f}, /* 12 */
{0.0f, 1.0f}, /* 13 */
{0.333333f, 1.0f}, /* 14 */
{0.666667f, 1.0f}, /* 15 */
{1.0f, 1.0f}, /* 16 */
}), TestSuite::Compare::Container);
} else CORRADE_VERIFY(!cone.hasAttribute(Trade::MeshAttribute::TextureCoordinates));
if(data.flags & ConeFlag::Tangents) {
auto tangents = cone.attribute<Vector4>(Trade::MeshAttribute::Tangent);
auto normals = cone.attribute<Vector3>(Trade::MeshAttribute::Normal);
for(std::size_t i = 0; i != tangents.size(); ++i) {
CORRADE_ITERATION(i);
CORRADE_ITERATION(tangents[i]);
CORRADE_ITERATION(normals[i]);
CORRADE_VERIFY(tangents[i].xyz().isNormalized());
CORRADE_VERIFY(normals[i].isNormalized());
CORRADE_COMPARE(Math::dot(tangents[i].xyz(), normals[i]), 0.0f);
}
}
/* Faces of the caps and sides do not share any vertices due to different
normals, each ring has an extra vertex for texture coords */

217
src/Magnum/Primitives/Test/CylinderTest.cpp

@ -26,7 +26,7 @@
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/TestSuite/Compare/Container.h>
#include "Magnum/Math/Vector3.h"
#include "Magnum/Math/Vector4.h"
#include "Magnum/Primitives/Cylinder.h"
#include "Magnum/Trade/MeshData.h"
@ -37,17 +37,30 @@ struct CylinderTest: TestSuite::Tester {
void solidWithoutAnything();
void solidWithCaps();
void solidWithTextureCoords();
void solidWithTextureCoordsAndCaps();
void solidWithTextureCoordinatesOrTangents();
void solidWithTextureCoordinatesOrTangentsAndCaps();
void wireframe();
};
constexpr struct {
const char* name;
CylinderFlags flags;
} TextureCoordinatesOrTangentsData[] {
{"texture coordinates", CylinderFlag::TextureCoordinates},
{"tangents", CylinderFlag::Tangents},
{"both", CylinderFlag::TextureCoordinates|CylinderFlag::Tangents}
};
CylinderTest::CylinderTest() {
addTests({&CylinderTest::solidWithoutAnything,
&CylinderTest::solidWithCaps,
&CylinderTest::solidWithTextureCoords,
&CylinderTest::solidWithTextureCoordsAndCaps,
&CylinderTest::wireframe});
&CylinderTest::solidWithCaps});
addInstancedTests({
&CylinderTest::solidWithTextureCoordinatesOrTangents,
&CylinderTest::solidWithTextureCoordinatesOrTangentsAndCaps},
Containers::arraySize(TextureCoordinatesOrTangentsData));
addTests({&CylinderTest::wireframe});
}
void CylinderTest::solidWithoutAnything() {
@ -162,12 +175,14 @@ void CylinderTest::solidWithCaps() {
}), TestSuite::Compare::Container);
}
void CylinderTest::solidWithTextureCoords() {
Trade::MeshData cylinder = cylinderSolid(2, 3, 1.5f, CylinderFlag::TextureCoordinates);
void CylinderTest::solidWithTextureCoordinatesOrTangents() {
auto&& data = TextureCoordinatesOrTangentsData[testCaseInstanceId()];
setTestCaseDescription(data.name);
Trade::MeshData cylinder = cylinderSolid(2, 3, 1.5f, data.flags);
CORRADE_COMPARE(cylinder.primitive(), MeshPrimitive::Triangles);
CORRADE_VERIFY(cylinder.isIndexed());
CORRADE_COMPARE(cylinder.attributeCount(), 3);
/* First vertex of each ring duplicated because it has different texture
coordinates */
@ -188,6 +203,25 @@ void CylinderTest::solidWithTextureCoords() {
{0.0f, 1.5f, 1.0f}, /* 11 */
}), TestSuite::Compare::Container);
if(data.flags & CylinderFlag::Tangents) {
CORRADE_COMPARE_AS(cylinder.attribute<Vector4>(Trade::MeshAttribute::Tangent), Containers::arrayView<Vector4>({
{1.0f, 0.0f, 0.0f, 1.0f}, /* 0 */
{-0.5f, 0.0f, -0.866025f, 1.0f}, /* 1 */
{-0.5f, 0.0f, 0.866025f, 1.0f}, /* 2 */
{1.0f, 0.0f, 0.0f, 1.0f}, /* 3 */
{1.0f, 0.0f, 0.0f, 1.0f}, /* 4 */
{-0.5f, 0.0f, -0.866025f, 1.0f}, /* 5 */
{-0.5f, 0.0f, 0.866025f, 1.0f}, /* 6 */
{1.0f, 0.0f, 0.0f, 1.0f}, /* 7 */
{1.0f, 0.0f, 0.0f, 1.0f}, /* 8 */
{-0.5f, 0.0f, -0.866025f, 1.0f}, /* 9 */
{-0.5f, 0.0f, 0.866025f, 1.0f}, /* 10 */
{1.0f, 0.0f, 0.0f, 1.0f} /* 11 */
}), TestSuite::Compare::Container);
} else CORRADE_VERIFY(!cylinder.hasAttribute(Trade::MeshAttribute::Tangent));
CORRADE_COMPARE_AS(cylinder.attribute<Vector3>(Trade::MeshAttribute::Normal), Containers::arrayView<Vector3>({
{0.0f, 0.0f, 1.0f}, /* 0 */
{0.866025f, 0.0f, -0.5f}, /* 1 */
@ -205,22 +239,37 @@ void CylinderTest::solidWithTextureCoords() {
{0.0f, 0.0f, 1.0f}, /* 11 */
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(cylinder.attribute<Vector2>(Trade::MeshAttribute::TextureCoordinates), Containers::arrayView<Vector2>({
{0.0f, 0.0f}, /* 0 */
{0.333333f, 0.0f}, /* 1 */
{0.666667f, 0.0f}, /* 2 */
{1.0f, 0.0f}, /* 3 */
{0.0f, 0.5f}, /* 4 */
{0.333333f, 0.5f}, /* 5 */
{0.666667f, 0.5f}, /* 6 */
{1.0f, 0.5f}, /* 7 */
{0.0f, 1.0f}, /* 8 */
{0.333333f, 1.0f}, /* 9 */
{0.666667f, 1.0f}, /* 10 */
{1.0f, 1.0f}, /* 11 */
}), TestSuite::Compare::Container);
if(data.flags & CylinderFlag::TextureCoordinates) {
CORRADE_COMPARE_AS(cylinder.attribute<Vector2>(Trade::MeshAttribute::TextureCoordinates), Containers::arrayView<Vector2>({
{0.0f, 0.0f}, /* 0 */
{0.333333f, 0.0f}, /* 1 */
{0.666667f, 0.0f}, /* 2 */
{1.0f, 0.0f}, /* 3 */
{0.0f, 0.5f}, /* 4 */
{0.333333f, 0.5f}, /* 5 */
{0.666667f, 0.5f}, /* 6 */
{1.0f, 0.5f}, /* 7 */
{0.0f, 1.0f}, /* 8 */
{0.333333f, 1.0f}, /* 9 */
{0.666667f, 1.0f}, /* 10 */
{1.0f, 1.0f}, /* 11 */
}), TestSuite::Compare::Container);
} else CORRADE_VERIFY(!cylinder.hasAttribute(Trade::MeshAttribute::TextureCoordinates));
if(data.flags & CylinderFlag::Tangents) {
auto tangents = cylinder.attribute<Vector4>(Trade::MeshAttribute::Tangent);
auto normals = cylinder.attribute<Vector3>(Trade::MeshAttribute::Normal);
for(std::size_t i = 0; i != tangents.size(); ++i) {
CORRADE_ITERATION(i);
CORRADE_ITERATION(tangents[i]);
CORRADE_ITERATION(normals[i]);
CORRADE_VERIFY(tangents[i].xyz().isNormalized());
CORRADE_VERIFY(normals[i].isNormalized());
CORRADE_COMPARE(Math::dot(tangents[i].xyz(), normals[i]), 0.0f);
}
}
/* Each ring has an extra vertex for texture coords */
CORRADE_COMPARE_AS(cylinder.indices<UnsignedInt>(), Containers::arrayView<UnsignedInt>({
@ -229,12 +278,14 @@ void CylinderTest::solidWithTextureCoords() {
}), TestSuite::Compare::Container);
}
void CylinderTest::solidWithTextureCoordsAndCaps() {
Trade::MeshData cylinder = cylinderSolid(2, 3, 1.5f, CylinderFlag::TextureCoordinates|CylinderFlag::CapEnds);
void CylinderTest::solidWithTextureCoordinatesOrTangentsAndCaps() {
auto&& data = TextureCoordinatesOrTangentsData[testCaseInstanceId()];
setTestCaseDescription(data.name);
Trade::MeshData cylinder = cylinderSolid(2, 3, 1.5f, data.flags|CylinderFlag::CapEnds);
CORRADE_COMPARE(cylinder.primitive(), MeshPrimitive::Triangles);
CORRADE_VERIFY(cylinder.isIndexed());
CORRADE_COMPARE(cylinder.attributeCount(), 3);
/* Bottom ring duplicated because it has different normals, first vertex of
each ring duplicated because it has different texture coordinates */
@ -269,6 +320,39 @@ void CylinderTest::solidWithTextureCoordsAndCaps() {
{0.0f, 1.5f, 0.0f} /* 21 */
}), TestSuite::Compare::Container);
if(data.flags & CylinderFlag::Tangents) {
CORRADE_COMPARE_AS(cylinder.attribute<Vector4>(Trade::MeshAttribute::Tangent), Containers::arrayView<Vector4>({
{-1.0f, 0.0f, 0.0f, 1.0f}, /* 0 */
{1.0f, 0.0f, 0.0f, 1.0f}, /* 1 */
{-0.5f, 0.0f, -0.866025f, 1.0f}, /* 2 */
{-0.5f, 0.0f, 0.866025f, 1.0f}, /* 3 */
{1.0f, 0.0f, 0.0f, 1.0f}, /* 4 */
{1.0f, 0.0f, 0.0f, 1.0f}, /* 5 */
{-0.5f, 0.0f, -0.866025f, 1.0f}, /* 6 */
{-0.5f, 0.0f, 0.866025f, 1.0f}, /* 7 */
{1.0f, 0.0f, 0.0f, 1.0f}, /* 8 */
{1.0f, 0.0f, 0.0f, 1.0f}, /* 9 */
{-0.5f, 0.0f, -0.866025f, 1.0f}, /* 10 */
{-0.5f, 0.0f, 0.866025f, 1.0f}, /* 11 */
{1.0f, 0.0f, 0.0f, 1.0f}, /* 12 */
{1.0f, 0.0f, 0.0f, 1.0f}, /* 13 */
{-0.5f, 0.0f, -0.866025f, 1.0f}, /* 14 */
{-0.5f, 0.0f, 0.866025f, 1.0f}, /* 15 */
{1.0f, 0.0f, 0.0f, 1.0f}, /* 16 */
{1.0f, 0.0f, 0.0f, 1.0f}, /* 17 */
{-0.5f, 0.0f, -0.866025f, 1.0f}, /* 18 */
{-0.5f, 0.0f, 0.866025f, 1.0f}, /* 19 */
{1.0f, 0.0f, 0.0f, 1.0f}, /* 20 */
{1.0f, 0.0f, 0.0f, 1.0f} /* 21 */
}), TestSuite::Compare::Container);
} else CORRADE_VERIFY(!cylinder.hasAttribute(Trade::MeshAttribute::Tangent));
CORRADE_COMPARE_AS(cylinder.attribute<Vector3>(Trade::MeshAttribute::Normal), Containers::arrayView<Vector3>({
{0.0f, -1.0f, 0.0f}, /* 0 */
@ -300,36 +384,51 @@ void CylinderTest::solidWithTextureCoordsAndCaps() {
{0.0f, 1.0f, 0.0f}, /* 21 */
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(cylinder.attribute<Vector2>(Trade::MeshAttribute::TextureCoordinates), Containers::arrayView<Vector2>({
{0.5f, 0.0f}, /* 0 */
{0.0f, 0.2f}, /* 1 */
{0.333333f, 0.2f}, /* 2 */
{0.666667f, 0.2f}, /* 3 */
{1.0f, 0.2f}, /* 4 */
{0.0f, 0.2f}, /* 5 */
{0.333333f, 0.2f}, /* 6 */
{0.666667f, 0.2f}, /* 7 */
{1.0f, 0.2f}, /* 8 */
{0.0f, 0.5f}, /* 9 */
{0.333333f, 0.5f}, /* 10 */
{0.666667f, 0.5f}, /* 11 */
{1.0f, 0.5f}, /* 12 */
{0.0f, 0.8f}, /* 13 */
{0.333333f, 0.8f}, /* 14 */
{0.666667f, 0.8f}, /* 15 */
{1.0f, 0.8f}, /* 16 */
{0.0f, 0.8f}, /* 17 */
{0.333333f, 0.8f}, /* 18 */
{0.666667f, 0.8f}, /* 19 */
{1.0f, 0.8f}, /* 20 */
{0.5f, 1.0f} /* 21 */
}), TestSuite::Compare::Container);
if(data.flags & CylinderFlag::TextureCoordinates) {
CORRADE_COMPARE_AS(cylinder.attribute<Vector2>(Trade::MeshAttribute::TextureCoordinates), Containers::arrayView<Vector2>({
{0.5f, 0.0f}, /* 0 */
{0.0f, 0.2f}, /* 1 */
{0.333333f, 0.2f}, /* 2 */
{0.666667f, 0.2f}, /* 3 */
{1.0f, 0.2f}, /* 4 */
{0.0f, 0.2f}, /* 5 */
{0.333333f, 0.2f}, /* 6 */
{0.666667f, 0.2f}, /* 7 */
{1.0f, 0.2f}, /* 8 */
{0.0f, 0.5f}, /* 9 */
{0.333333f, 0.5f}, /* 10 */
{0.666667f, 0.5f}, /* 11 */
{1.0f, 0.5f}, /* 12 */
{0.0f, 0.8f}, /* 13 */
{0.333333f, 0.8f}, /* 14 */
{0.666667f, 0.8f}, /* 15 */
{1.0f, 0.8f}, /* 16 */
{0.0f, 0.8f}, /* 17 */
{0.333333f, 0.8f}, /* 18 */
{0.666667f, 0.8f}, /* 19 */
{1.0f, 0.8f}, /* 20 */
{0.5f, 1.0f} /* 21 */
}), TestSuite::Compare::Container);
} else CORRADE_VERIFY(!cylinder.hasAttribute(Trade::MeshAttribute::TextureCoordinates));
if(data.flags & CylinderFlag::Tangents) {
auto tangents = cylinder.attribute<Vector4>(Trade::MeshAttribute::Tangent);
auto normals = cylinder.attribute<Vector3>(Trade::MeshAttribute::Normal);
for(std::size_t i = 0; i != tangents.size(); ++i) {
CORRADE_ITERATION(i);
CORRADE_ITERATION(tangents[i]);
CORRADE_ITERATION(normals[i]);
CORRADE_VERIFY(tangents[i].xyz().isNormalized());
CORRADE_VERIFY(normals[i].isNormalized());
CORRADE_COMPARE(Math::dot(tangents[i].xyz(), normals[i]), 0.0f);
}
}
/* Faces of the caps and sides do not share any vertices due to different
normals, each ring has an extra vertex for texture coords */

97
src/Magnum/Primitives/Test/UVSphereTest.cpp

@ -26,7 +26,7 @@
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/TestSuite/Compare/Container.h>
#include "Magnum/Math/Vector3.h"
#include "Magnum/Math/Vector4.h"
#include "Magnum/Primitives/UVSphere.h"
#include "Magnum/Trade/MeshData.h"
@ -35,18 +35,30 @@ namespace Magnum { namespace Primitives { namespace Test { namespace {
struct UVSphereTest: TestSuite::Tester {
explicit UVSphereTest();
void solidWithoutTextureCoords();
void solidWithTextureCoords();
void solidWithoutTextureCoordinates();
void solidWithTextureCoordinatesOrTangents();
void wireframe();
};
constexpr struct {
const char* name;
UVSphereFlags flags;
} TextureCoordinatesOrTangentsData[] {
{"texture coordinates", UVSphereFlag::TextureCoordinates},
{"tangents", UVSphereFlag::Tangents},
{"both", UVSphereFlag::TextureCoordinates|UVSphereFlag::Tangents}
};
UVSphereTest::UVSphereTest() {
addTests({&UVSphereTest::solidWithoutTextureCoords,
&UVSphereTest::solidWithTextureCoords,
&UVSphereTest::wireframe});
addTests({&UVSphereTest::solidWithoutTextureCoordinates});
addInstancedTests({&UVSphereTest::solidWithTextureCoordinatesOrTangents},
Containers::arraySize(TextureCoordinatesOrTangentsData));
addTests({&UVSphereTest::wireframe});
}
void UVSphereTest::solidWithoutTextureCoords() {
void UVSphereTest::solidWithoutTextureCoordinates() {
Trade::MeshData sphere = uvSphereSolid(3, 3);
CORRADE_COMPARE(sphere.primitive(), MeshPrimitive::Triangles);
@ -88,12 +100,14 @@ void UVSphereTest::solidWithoutTextureCoords() {
}), TestSuite::Compare::Container);
}
void UVSphereTest::solidWithTextureCoords() {
Trade::MeshData sphere = uvSphereSolid(3, 3, UVSphereFlag::TextureCoordinates);
void UVSphereTest::solidWithTextureCoordinatesOrTangents() {
auto&& data = TextureCoordinatesOrTangentsData[testCaseInstanceId()];
setTestCaseDescription(data.name);
Trade::MeshData sphere = uvSphereSolid(3, 3, data.flags);
CORRADE_COMPARE(sphere.primitive(), MeshPrimitive::Triangles);
CORRADE_VERIFY(sphere.isIndexed());
CORRADE_COMPARE(sphere.attributeCount(), 3);
CORRADE_COMPARE_AS(sphere.attribute<Vector3>(Trade::MeshAttribute::Position), Containers::arrayView<Vector3>({
{0.0f, -1.0f, 0.0f},
@ -111,21 +125,54 @@ void UVSphereTest::solidWithTextureCoords() {
{0.0f, 1.0f, 0.0f}
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(sphere.attribute<Vector2>(Trade::MeshAttribute::TextureCoordinates), Containers::arrayView<Vector2>({
{0.5f, 0.0f},
{0.0f, 0.333333f},
{0.333333f, 0.333333f},
{0.666667f, 0.333333f},
{1.0f, 0.333333f},
{0.0f, 0.666667f},
{0.333333f, 0.666667f},
{0.666667f, 0.666667f},
{1.0f, 0.666667f},
{0.5f, 1.0f}
}), TestSuite::Compare::Container);
if(data.flags & UVSphereFlag::Tangents) {
CORRADE_COMPARE_AS(sphere.attribute<Vector4>(Trade::MeshAttribute::Tangent), Containers::arrayView<Vector4>({
{-1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{-0.5f, 0.0f, -0.866025f, 1.0f},
{-0.5f, 0.0f, 0.866025f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{-0.5f, 0.0f, -0.866025f, 1.0f},
{-0.5f, 0.0f, 0.866025f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f},
{1.0f, 0.0f, 0.0f, 1.0f}
}), TestSuite::Compare::Container);
} else CORRADE_VERIFY(!sphere.hasAttribute(Trade::MeshAttribute::Tangent));
if(data.flags & UVSphereFlag::TextureCoordinates) {
CORRADE_COMPARE_AS(sphere.attribute<Vector2>(Trade::MeshAttribute::TextureCoordinates), Containers::arrayView<Vector2>({
{0.5f, 0.0f},
{0.0f, 0.333333f},
{0.333333f, 0.333333f},
{0.666667f, 0.333333f},
{1.0f, 0.333333f},
{0.0f, 0.666667f},
{0.333333f, 0.666667f},
{0.666667f, 0.666667f},
{1.0f, 0.666667f},
{0.5f, 1.0f}
}), TestSuite::Compare::Container);
} else CORRADE_VERIFY(!sphere.hasAttribute(Trade::MeshAttribute::TextureCoordinates));
if(data.flags & UVSphereFlag::Tangents) {
auto tangents = sphere.attribute<Vector4>(Trade::MeshAttribute::Tangent);
auto normals = sphere.attribute<Vector3>(Trade::MeshAttribute::Normal);
for(std::size_t i = 0; i != tangents.size(); ++i) {
CORRADE_ITERATION(i);
CORRADE_ITERATION(tangents[i]);
CORRADE_ITERATION(normals[i]);
CORRADE_VERIFY(tangents[i].xyz().isNormalized());
CORRADE_VERIFY(normals[i].isNormalized());
CORRADE_COMPARE(Math::dot(tangents[i].xyz(), normals[i]), 0.0f);
}
}
CORRADE_COMPARE_AS(sphere.indices<UnsignedInt>(), Containers::arrayView<UnsignedInt>({
0, 2, 1, 0, 3, 2, 0, 4, 3,

17
src/Magnum/Primitives/UVSphere.h

@ -44,7 +44,15 @@ namespace Magnum { namespace Primitives {
@see @ref UVSphereFlags, @ref uvSphereSolid()
*/
enum class UVSphereFlag: UnsignedByte {
TextureCoordinates = 1 << 0 /**< Generate texture coordinates */
TextureCoordinates = 1 << 0, /**< Generate texture coordinates */
/**
* Generate four-component tangents. The last component can be used to
* reconstruct a bitangent as described in the documentation of
* @ref Trade::MeshAttribute::Tangent.
* @m_since_latest
*/
Tangents = 1 << 1
};
/**
@ -68,9 +76,10 @@ CORRADE_ENUMSET_OPERATORS(UVSphereFlags)
Sphere with radius @cpp 1.0f @ce. @ref MeshPrimitive::Triangles with
@ref MeshIndexType::UnsignedInt indices, interleaved @ref VertexFormat::Vector3
positions, @ref VertexFormat::Vector3 normals and optional
@ref VertexFormat::Vector2 texture coordinates. If texture coordinates are
generated, vertices of one segment are duplicated for texture wrapping.
positions, @ref VertexFormat::Vector3 normals, optional
@ref VertexFormat::Vector4 tangents and @ref VertexFormat::Vector2 texture
coordinates. If texture coordinates are generated, vertices of one segment are
duplicated for texture wrapping.
@image html primitives-uvspheresolid.png width=256px

Loading…
Cancel
Save