Browse Source

Primitives: port away from MeshDataXD.

The internals don't use any std::vector anymore, only the icosphere
needs an std::unordered_map to do duplicate removal. Additionally, the
most simple primitives are now simply views on constant data,
being completely zero-allocation.

On a Mac this resulted in the dylib going down from 1.5 MB to 418 kB in
Debug, and from 129 kB to 90 kB in Release. Quite nice.

The tests are not ported away from MeshDataXD yet as I want to ensure
the behavior is *exactly* as before.
pull/371/head
Vladimír Vondruš 7 years ago
parent
commit
c8de337c06
  1. 10
      doc/generated/primitives.cpp
  2. 3
      doc/snippets/MagnumPrimitives.cpp
  3. 160
      src/Magnum/Primitives/Axis.cpp
  4. 16
      src/Magnum/Primitives/Axis.h
  5. 56
      src/Magnum/Primitives/Capsule.cpp
  6. 25
      src/Magnum/Primitives/Capsule.h
  7. 150
      src/Magnum/Primitives/Circle.cpp
  8. 22
      src/Magnum/Primitives/Circle.h
  9. 10
      src/Magnum/Primitives/Cone.cpp
  10. 23
      src/Magnum/Primitives/Cone.h
  11. 41
      src/Magnum/Primitives/Crosshair.cpp
  12. 12
      src/Magnum/Primitives/Crosshair.h
  13. 224
      src/Magnum/Primitives/Cube.cpp
  14. 22
      src/Magnum/Primitives/Cube.h
  15. 10
      src/Magnum/Primitives/Cylinder.cpp
  16. 24
      src/Magnum/Primitives/Cylinder.h
  17. 86
      src/Magnum/Primitives/Gradient.cpp
  18. 28
      src/Magnum/Primitives/Gradient.h
  19. 159
      src/Magnum/Primitives/Grid.cpp
  20. 19
      src/Magnum/Primitives/Grid.h
  21. 131
      src/Magnum/Primitives/Icosphere.cpp
  22. 7
      src/Magnum/Primitives/Icosphere.h
  23. 220
      src/Magnum/Primitives/Implementation/Spheroid.cpp
  24. 19
      src/Magnum/Primitives/Implementation/Spheroid.h
  25. 64
      src/Magnum/Primitives/Implementation/WireframeSpheroid.cpp
  26. 8
      src/Magnum/Primitives/Implementation/WireframeSpheroid.h
  27. 27
      src/Magnum/Primitives/Line.cpp
  28. 14
      src/Magnum/Primitives/Line.h
  29. 105
      src/Magnum/Primitives/Plane.cpp
  30. 12
      src/Magnum/Primitives/Plane.h
  31. 86
      src/Magnum/Primitives/Square.cpp
  32. 13
      src/Magnum/Primitives/Square.h
  33. 10
      src/Magnum/Primitives/UVSphere.cpp
  34. 14
      src/Magnum/Primitives/UVSphere.h
  35. 3
      src/Magnum/Trade/MeshData.h

10
doc/generated/primitives.cpp

@ -452,7 +452,7 @@ std::pair<Trade::MeshData3D, std::string> PrimitiveVisualizer::gradient3DVertica
}
std::pair<Trade::MeshData2D, std::string> PrimitiveVisualizer::capsule2DWireframe() {
auto capsule = Primitives::capsule2DWireframe(8, 1, 0.75f);
Trade::MeshData2D capsule = Primitives::capsule2DWireframe(8, 1, 0.75f);
MeshTools::transformPointsInPlace(Matrix3::scaling(Vector2{0.75f}), capsule.positions(0));
return {std::move(capsule), "capsule2dwireframe.png"};
}
@ -466,7 +466,7 @@ std::pair<Trade::MeshData2D, std::string> PrimitiveVisualizer::crosshair2D() {
}
std::pair<Trade::MeshData2D, std::string> PrimitiveVisualizer::line2D() {
auto line = Primitives::line2D();
Trade::MeshData2D line = Primitives::line2D();
MeshTools::transformPointsInPlace(Matrix3::translation(Vector2::xAxis(-1.0f))*Matrix3::scaling(Vector2::xScale(2.0f)), line.positions(0));
return {std::move(line), "line2d.png"};
}
@ -476,7 +476,7 @@ std::pair<Trade::MeshData2D, std::string> PrimitiveVisualizer::squareWireframe()
}
std::pair<Trade::MeshData3D, std::string> PrimitiveVisualizer::capsule3DWireframe() {
auto capsule = Primitives::capsule3DWireframe(8, 1, 16, 1.0f);
Trade::MeshData3D capsule = Primitives::capsule3DWireframe(8, 1, 16, 1.0f);
MeshTools::transformPointsInPlace(Matrix4::scaling(Vector3{0.75f}), capsule.positions(0));
return {std::move(capsule), "capsule3dwireframe.png"};
}
@ -506,7 +506,7 @@ std::pair<Trade::MeshData3D, std::string> PrimitiveVisualizer::grid3DWireframe()
}
std::pair<Trade::MeshData3D, std::string> PrimitiveVisualizer::line3D() {
auto line = Primitives::line3D();
Trade::MeshData3D line = Primitives::line3D();
MeshTools::transformPointsInPlace(Matrix4::translation(Vector3::xAxis(-1.0f))*Matrix4::scaling(Vector3::xScale(2.0f)), line.positions(0));
return {std::move(line), "line3d.png"};
}
@ -528,7 +528,7 @@ std::pair<Trade::MeshData2D, std::string> PrimitiveVisualizer::squareSolid() {
}
std::pair<Trade::MeshData3D, std::string> PrimitiveVisualizer::capsule3DSolid() {
auto capsule = Primitives::capsule3DSolid(4, 1, 12, 0.75f);
Trade::MeshData3D capsule = Primitives::capsule3DSolid(4, 1, 12, 0.75f);
MeshTools::transformPointsInPlace(Matrix4::scaling(Vector3{0.75f}), capsule.positions(0));
return {std::move(capsule), "capsule3dsolid.png"};
}

3
doc/snippets/MagnumPrimitives.cpp

@ -26,8 +26,7 @@
#include "Magnum/Math/Color.h"
#include "Magnum/Primitives/Gradient.h"
#include "Magnum/Primitives/Line.h"
#include "Magnum/Trade/MeshData2D.h"
#include "Magnum/Trade/MeshData3D.h"
#include "Magnum/Trade/MeshData.h"
using namespace Magnum;

160
src/Magnum/Primitives/Axis.cpp

@ -27,83 +27,99 @@
#include "Magnum/Mesh.h"
#include "Magnum/Math/Color.h"
#include "Magnum/Trade/MeshData2D.h"
#include "Magnum/Trade/MeshData3D.h"
#include "Magnum/Trade/MeshData.h"
namespace Magnum { namespace Primitives {
using namespace Math::Literals;
Trade::MeshData2D axis2D() {
return Trade::MeshData2D{MeshPrimitive::Lines,
{0, 1,
1, 2, /* X axis */
1, 3,
4, 5,
5, 6, /* Y axis */
5, 7},
{{{ 0.0f, 0.0f},
{ 1.0f, 0.0f}, /* X axis */
{ 0.9f, 0.1f},
{ 0.9f, -0.1f},
{ 0.0f, 0.0f},
{ 0.0f, 1.0f}, /* Y axis */
{ 0.1f, 0.9f},
{-0.1f, 0.9f}}}, {},
{{0xff0000_rgbf,
0xff0000_rgbf, /* X axis */
0xff0000_rgbf,
0xff0000_rgbf,
0x00ff00_rgbf,
0x00ff00_rgbf, /* Y axis */
0x00ff00_rgbf,
0x00ff00_rgbf}}};
namespace {
/* not 8-bit because GPUs (and Vulkan) don't like it nowadays */
constexpr UnsignedShort Indices2D[]{
0, 1,
1, 2, /* X axis */
1, 3,
4, 5,
5, 6, /* Y axis */
5, 7
};
constexpr UnsignedShort Indices3D[]{
0, 1,
1, 2, /* X axis */
1, 3,
4, 5,
5, 6, /* Y axis */
5, 7,
8, 9,
9, 10, /* Z axis */
9, 11
};
constexpr struct Vertex2D {
Vector2 position;
Color3 color;
} Vertices2D[]{
{{ 0.0f, 0.0f}, {1.0f, 0.0f, 0.0f}},
{{ 1.0f, 0.0f}, {1.0f, 0.0f, 0.0f}}, /* X axis */
{{ 0.9f, 0.1f}, {1.0f, 0.0f, 0.0f}},
{{ 0.9f, -0.1f}, {1.0f, 0.0f, 0.0f}},
{{ 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}},
{{ 0.0f, 1.0f}, {0.0f, 1.0f, 0.0f}}, /* Y axis */
{{ 0.1f, 0.9f}, {0.0f, 1.0f, 0.0f}},
{{-0.1f, 0.9f}, {0.0f, 1.0f, 0.0f}}
};
constexpr struct Vertex3D {
Vector3 position;
Color3 color;
} Vertices3D[]{
{{ 0.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 0.0f}},
{{ 1.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 0.0f}}, /* X axis */
{{ 0.9f, 0.1f, 0.0f}, {1.0f, 0.0f, 0.0f}},
{{ 0.9f, -0.1f, 0.0f}, {1.0f, 0.0f, 0.0f}},
{{ 0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f}},
{{ 0.0f, 1.0f, 0.0f}, {0.0f, 1.0f, 0.0f}}, /* Y axis */
{{ 0.1f, 0.9f, 0.0f}, {0.0f, 1.0f, 0.0f}},
{{-0.1f, 0.9f, 0.0f}, {0.0f, 1.0f, 0.0f}},
{{ 0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f}},
{{ 0.0f, 0.0f, 1.0f}, {0.0f, 0.0f, 1.0f}}, /* Z axis */
{{ 0.1f, 0.0f, 0.9f}, {0.0f, 0.0f, 1.0f}},
{{-0.1f, 0.0f, 0.9f}, {0.0f, 0.0f, 1.0f}}
};
constexpr Trade::MeshAttributeData Attributes2D[]{
Trade::MeshAttributeData{Trade::MeshAttribute::Position,
Containers::stridedArrayView(Vertices2D, &Vertices2D[0].position,
Containers::arraySize(Vertices2D), sizeof(Vertex2D))},
Trade::MeshAttributeData{Trade::MeshAttribute::Color,
Containers::stridedArrayView(Vertices2D, &Vertices2D[0].color,
Containers::arraySize(Vertices2D), sizeof(Vertex2D))}
};
constexpr Trade::MeshAttributeData Attributes3D[]{
Trade::MeshAttributeData{Trade::MeshAttribute::Position,
Containers::stridedArrayView(Vertices3D, &Vertices3D[0].position,
Containers::arraySize(Vertices3D), sizeof(Vertex3D))},
Trade::MeshAttributeData{Trade::MeshAttribute::Color,
Containers::stridedArrayView(Vertices3D, &Vertices3D[0].color,
Containers::arraySize(Vertices3D), sizeof(Vertex3D))}
};
}
Trade::MeshData axis2D() {
return Trade::MeshData{MeshPrimitive::Lines,
{}, Indices2D, Trade::MeshIndexData{Indices2D},
{}, Vertices2D, Trade::meshAttributeDataNonOwningArray(Attributes2D)};
}
Trade::MeshData3D axis3D() {
return Trade::MeshData3D{MeshPrimitive::Lines,
{0, 1,
1, 2, /* X axis */
1, 3,
4, 5,
5, 6, /* Y axis */
5, 7,
8, 9,
9, 10, /* Z axis */
9, 11},
{{{ 0.0f, 0.0f, 0.0f},
{ 1.0f, 0.0f, 0.0f}, /* X axis */
{ 0.9f, 0.1f, 0.0f},
{ 0.9f, -0.1f, 0.0f},
{ 0.0f, 0.0f, 0.0f},
{ 0.0f, 1.0f, 0.0f}, /* Y axis */
{ 0.1f, 0.9f, 0.0f},
{-0.1f, 0.9f, 0.0f},
{ 0.0f, 0.0f, 0.0f},
{ 0.0f, 0.0f, 1.0f}, /* Z axis */
{ 0.1f, 0.0f, 0.9f},
{-0.1f, 0.0f, 0.9f}}}, {}, {},
{{0xff0000_rgbf,
0xff0000_rgbf, /* X axis */
0xff0000_rgbf,
0xff0000_rgbf,
0x00ff00_rgbf,
0x00ff00_rgbf, /* Y axis */
0x00ff00_rgbf,
0x00ff00_rgbf,
0x0000ff_rgbf,
0x0000ff_rgbf, /* Z axis */
0x0000ff_rgbf,
0x0000ff_rgbf}}};
Trade::MeshData axis3D() {
return Trade::MeshData{MeshPrimitive::Lines,
{}, Indices3D, Trade::MeshIndexData{Indices3D},
{}, Vertices3D, Trade::meshAttributeDataNonOwningArray(Attributes3D)};
}
}}

16
src/Magnum/Primitives/Axis.h

@ -37,26 +37,30 @@ namespace Magnum { namespace Primitives {
/**
@brief 2D axis
Two color-coded arrows for visualizing orientation (XY is RG). Indexed
@ref MeshPrimitive::Lines with vertex colors.
Two color-coded arrows for visualizing orientation (XY is RG).
@ref MeshPrimitive::Lines with @ref MeshIndexType::UnsignedShort indices,
interleaved @ref VertexFormat::Vector2 positions and @ref VertexFormat::Vector3
colors. The returned instance references data stored in constant memory.
@image html primitives-axis2d.png width=256px
@see @ref axis3D(), @ref crosshair2D(), @ref line2D()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData2D axis2D();
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData axis2D();
/**
@brief 3D axis
Three color-coded arrows for visualizing orientation (XYZ is RGB). Indexed
@ref MeshPrimitive::Lines with vertex colors.
Three color-coded arrows for visualizing orientation (XYZ is RGB).
@ref MeshPrimitive::Lines with @ref MeshIndexType::UnsignedShort indices,
interleaved @ref VertexFormat::Vector3 positions and @ref VertexFormat::Vector3
colors. The returned instance references data stored in constant memory.
@image html primitives-axis3d.png width=256px
@see @ref axis2D(), @ref crosshair3D(), @ref line3D()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D axis3D();
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData axis3D();
}}

56
src/Magnum/Primitives/Capsule.cpp

@ -25,28 +25,29 @@
#include "Capsule.h"
#include <Corrade/Containers/GrowableArray.h>
#include "Magnum/Math/Color.h"
#include "Magnum/Math/Functions.h"
#include "Magnum/Mesh.h"
#include "Magnum/Primitives/Implementation/Spheroid.h"
#include "Magnum/Primitives/Implementation/WireframeSpheroid.h"
#include "Magnum/Trade/MeshData2D.h"
#include "Magnum/Trade/MeshData3D.h"
#include "Magnum/Trade/MeshData.h"
namespace Magnum { namespace Primitives {
Trade::MeshData2D capsule2DWireframe(const UnsignedInt hemisphereRings, const UnsignedInt cylinderRings, const Float halfLength) {
Trade::MeshData capsule2DWireframe(const UnsignedInt hemisphereRings, const UnsignedInt cylinderRings, const Float halfLength) {
CORRADE_ASSERT(hemisphereRings >= 1 && cylinderRings >= 1,
"Primitives::capsule2DWireframe(): at least one hemisphere ring and one cylinder ring expected",
(Trade::MeshData2D{MeshPrimitive::Triangles, {}, {}, {}, {}, nullptr}));
(Trade::MeshData{MeshPrimitive::Triangles, 0}));
std::vector<Vector2> positions;
positions.reserve(hemisphereRings*4+2+(cylinderRings-1)*2);
Containers::Array<Vector2> vertexData;
arrayReserve(vertexData, hemisphereRings*4+2+(cylinderRings-1)*2);
const Rad angleIncrement(Constants::piHalf()/hemisphereRings);
const Float cylinderIncrement = 2.0f*halfLength/cylinderRings;
/* Bottom cap vertex */
positions.emplace_back(0.0f, -halfLength-1.0f);
arrayAppend(vertexData, {0.0f, -halfLength-1.0f});
/* Bottom hemisphere */
for(UnsignedInt i = 0; i != hemisphereRings; ++i) {
@ -54,13 +55,13 @@ Trade::MeshData2D capsule2DWireframe(const UnsignedInt hemisphereRings, const Un
const std::pair<Float, Float> sincos = Math::sincos(angle);
const Float x = sincos.first;
const Float y = -sincos.second-halfLength;
positions.insert(positions.end(), {{-x, y}, {x, y}});
arrayAppend(vertexData, {{-x, y}, {x, y}});
}
/* Cylinder (bottom and top vertices are done within caps */
/* Cylinder (bottom and top vertices are done within caps) */
for(UnsignedInt i = 0; i != cylinderRings-1; ++i) {
const Float y = (i+1)*cylinderIncrement-halfLength;
positions.insert(positions.end(), {{-1.0f, y}, {1.0f, y}});
arrayAppend(vertexData, {{-1.0f, y}, {1.0f, y}});
}
/* Top hemisphere */
@ -69,35 +70,38 @@ Trade::MeshData2D capsule2DWireframe(const UnsignedInt hemisphereRings, const Un
const std::pair<Float, Float> sincos = Math::sincos(angle);
const Float x = sincos.second;
const Float y = sincos.first+halfLength;
positions.insert(positions.end(), {{-x, y}, {x, y}});
arrayAppend(vertexData, {{-x, y}, {x, y}});
}
/* Top cap vertex */
positions.emplace_back(0.0f, halfLength+1.0f);
arrayAppend(vertexData, {0.0f, halfLength+1.0f});
std::vector<UnsignedInt> indices;
indices.reserve(hemisphereRings*8+cylinderRings*4);
Containers::Array<UnsignedInt> indexData;
arrayReserve(indexData, hemisphereRings*8+cylinderRings*4);
/* Bottom cap indices */
indices.insert(indices.end(), {0, 1, 0, 2});
arrayAppend(indexData, {0u, 1u, 0u, 2u});
/* Side indices */
for(UnsignedInt i = 0; i != cylinderRings+hemisphereRings*2-2; ++i)
indices.insert(indices.end(), {i*2+1, i*2+3,
i*2+2, i*2+4});
arrayAppend(indexData, {i*2+1, i*2+3, i*2+2, i*2+4});
/* Top cap indices */
indices.insert(indices.end(),
{UnsignedInt(positions.size())-3, UnsignedInt(positions.size())-1,
UnsignedInt(positions.size())-2, UnsignedInt(positions.size())-1});
return Trade::MeshData2D{MeshPrimitive::Lines, std::move(indices), {std::move(positions)}, {}, {}, nullptr};
arrayAppend(indexData,
{UnsignedInt(vertexData.size())-3, UnsignedInt(vertexData.size())-1,
UnsignedInt(vertexData.size())-2, UnsignedInt(vertexData.size())-1});
Trade::MeshIndexData indices{indexData};
Trade::MeshAttributeData positions{Trade::MeshAttribute::Position, Containers::arrayView(vertexData)};
return Trade::MeshData{MeshPrimitive::Lines,
Containers::arrayAllocatorCast<char>(std::move(indexData)), indices,
Containers::arrayAllocatorCast<char>(std::move(vertexData)), {positions}};
}
Trade::MeshData3D capsule3DSolid(const UnsignedInt hemisphereRings, const UnsignedInt cylinderRings, const UnsignedInt segments, const Float halfLength, const CapsuleTextureCoords textureCoords) {
Trade::MeshData capsule3DSolid(const UnsignedInt hemisphereRings, const UnsignedInt cylinderRings, const UnsignedInt segments, const Float halfLength, const CapsuleTextureCoords textureCoords) {
CORRADE_ASSERT(hemisphereRings >= 1 && cylinderRings >= 1 && segments >= 3,
"Primitives::capsule3DSolid(): at least one hemisphere ring, one cylinder ring and three segments expected",
(Trade::MeshData3D{MeshPrimitive::Triangles, {}, {}, {}, {}, {}, nullptr}));
(Trade::MeshData{MeshPrimitive::Triangles, 0}));
Implementation::Spheroid capsule(segments, textureCoords == CapsuleTextureCoords::Generate ?
Implementation::Spheroid::TextureCoords::Generate :
@ -130,10 +134,10 @@ Trade::MeshData3D capsule3DSolid(const UnsignedInt hemisphereRings, const Unsign
return capsule.finalize();
}
Trade::MeshData3D capsule3DWireframe(const UnsignedInt hemisphereRings, const UnsignedInt cylinderRings, const UnsignedInt segments, const Float halfLength) {
Trade::MeshData capsule3DWireframe(const UnsignedInt hemisphereRings, const UnsignedInt cylinderRings, const UnsignedInt segments, const Float halfLength) {
CORRADE_ASSERT(hemisphereRings >= 1 && cylinderRings >= 1 && segments >= 4 && segments%4 == 0,
"Primitives::capsule3DWireframe(): at least one hemisphere and cylinder ring and multiples of 4 segments expected",
(Trade::MeshData3D{MeshPrimitive::Triangles, {}, {}, {}, {}, {}, nullptr}));
(Trade::MeshData{MeshPrimitive::Triangles, 0}));
Implementation::WireframeSpheroid capsule(segments/4);

25
src/Magnum/Primitives/Capsule.h

@ -43,14 +43,15 @@ namespace Magnum { namespace Primitives {
@param halfLength Half the length of cylinder part
Cylinder of radius @cpp 1.0f @ce along Y axis with hemispheres instead of caps.
Indexed @ref MeshPrimitive::Lines.
@ref MeshPrimitive::Lines with @ref MeshIndexType::UnsignedInt indices and
@ref VertexFormat::Vector2 positions.
@image html primitives-capsule2dwireframe.png width=256px
@see @ref capsule3DSolid(), @ref capsule3DWireframe(), @ref circle2DWireframe(),
@ref squareWireframe()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData2D capsule2DWireframe(UnsignedInt hemisphereRings, UnsignedInt cylinderRings, Float halfLength);
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData capsule2DWireframe(UnsignedInt hemisphereRings, UnsignedInt cylinderRings, Float halfLength);
/**
@brief Whether to generate capsule texture coordinates
@ -74,20 +75,21 @@ enum class CapsuleTextureCoords: UnsignedByte {
@param textureCoords Whether to generate texture coordinates
Cylinder of radius @cpp 1.0f @ce along Y axis with hemispheres instead of caps.
Indexed @ref MeshPrimitive::Triangles with normals and optional 2D texture
coordinates. If texture coordinates are generated, vertices of one segment are
duplicated for texture wrapping.
@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.
@image html primitives-capsule3dsolid.png width=256px
The capsule is by default created with radius set to @f$ 1.0 @f$. In order to
get radius @f$ r @f$, length @f$ l @f$ and preserve correct normals, set
@p halfLength to @f$ 0.5 \frac{l}{r} @f$ and then scale all
@ref Trade::MeshData3D::positions() by @f$ r @f$, for example using
@ref MeshTools::transformPointsInPlace().
@p halfLength to @f$ 0.5 \frac{l}{r} @f$ and then scale all positions by
@f$ r @f$, for example using @ref MeshTools::transformPointsInPlace().
@see @ref capsule3DWireframe(), @ref capsule2DWireframe(), @ref cylinderSolid()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D capsule3DSolid(UnsignedInt hemisphereRings, UnsignedInt cylinderRings, UnsignedInt segments, Float halfLength, CapsuleTextureCoords textureCoords = CapsuleTextureCoords::DontGenerate);
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData capsule3DSolid(UnsignedInt hemisphereRings, UnsignedInt cylinderRings, UnsignedInt segments, Float halfLength, CapsuleTextureCoords textureCoords = CapsuleTextureCoords::DontGenerate);
/**
@brief Wireframe 3D capsule
@ -100,13 +102,14 @@ MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D capsule3DSolid(UnsignedInt hemisphere
@param halfLength Half the length of cylinder part
Cylinder of radius @cpp 1.0f @ce along Y axis with hemispheres instead of caps.
Indexed @ref MeshPrimitive::Lines.
@ref MeshPrimitive::Lines with @ref MeshIndexType::UnsignedInt indices and
@ref VertexFormat::Vector3 positions.
@image html primitives-capsule3dwireframe.png width=256px
@see @ref capsule2DWireframe(), @ref capsule3DSolid(), @ref cylinderSolid()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D capsule3DWireframe(UnsignedInt hemisphereRings, UnsignedInt cylinderRings, UnsignedInt segments, Float halfLength);
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData capsule3DWireframe(UnsignedInt hemisphereRings, UnsignedInt cylinderRings, UnsignedInt segments, Float halfLength);
}}

150
src/Magnum/Primitives/Circle.cpp

@ -28,112 +28,140 @@
#include "Magnum/Math/Functions.h"
#include "Magnum/Math/Color.h"
#include "Magnum/Mesh.h"
#include "Magnum/Trade/MeshData2D.h"
#include "Magnum/Trade/MeshData3D.h"
#include "Magnum/Trade/MeshData.h"
namespace Magnum { namespace Primitives {
Trade::MeshData2D circle2DSolid(const UnsignedInt segments, CircleTextureCoords textureCoords) {
Trade::MeshData circle2DSolid(const UnsignedInt segments, const CircleTextureCoords textureCoords) {
CORRADE_ASSERT(segments >= 3, "Primitives::circle2DSolid(): segments must be >= 3",
(Trade::MeshData2D{MeshPrimitive::TriangleFan, {}, {}, {}, {}, nullptr}));
std::vector<Vector2> positions;
positions.reserve(segments + 2);
std::vector<std::vector<Vector2>> textureCoordinates;
if(textureCoords == CircleTextureCoords::Generate)
textureCoordinates.emplace_back();
/* Central point */
positions.emplace_back();
if(textureCoords == CircleTextureCoords::Generate)
textureCoordinates.front().emplace_back(0.5f, 0.5f);
/* Points on circle. The first/last point is here twice to close the circle
properly. */
(Trade::MeshData{MeshPrimitive::TriangleFan, 0}));
/* Allocate interleaved array for all vertex data */
std::size_t stride = sizeof(Vector2);
std::size_t attributeCount = 1;
if(textureCoords == CircleTextureCoords::Generate) {
++attributeCount;
stride += sizeof(Vector2);
}
Containers::Array<char> vertexData{stride*(segments + 2)};
Containers::Array<Trade::MeshAttributeData> attributes{attributeCount};
/* Fill positions */
Containers::StridedArrayView1D<Vector2> positions{vertexData,
reinterpret_cast<Vector2*>(vertexData.begin()),
segments + 2, std::ptrdiff_t(stride)};
attributes[0] =
Trade::MeshAttributeData{Trade::MeshAttribute::Position, positions};
positions[0] = {};
/* Points on the circle. The first/last point is here twice to close the
circle properly. */
const Rad angleIncrement(Constants::tau()/segments);
for(UnsignedInt i = 0; i != segments + 1; ++i) {
const Rad angle(Float(i)*angleIncrement);
const std::pair<Float, Float> sincos = Math::sincos(angle);
Vector2 position{sincos.second, sincos.first};
positions.emplace_back(position);
positions[i + 1] = {sincos.second, sincos.first};
}
if(textureCoords == CircleTextureCoords::Generate)
textureCoordinates.front().emplace_back(position*0.5f + Vector2{0.5f});
/* Fill texture coords, if any */
if(textureCoords == CircleTextureCoords::Generate) {
Containers::StridedArrayView1D<Vector2> textureCoords{vertexData,
reinterpret_cast<Vector2*>(vertexData.begin() + sizeof(Vector2)),
positions.size(), std::ptrdiff_t(stride)};
attributes[1] =
Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates, textureCoords};
for(std::size_t i = 0; i != positions.size(); ++i)
textureCoords[i] = positions[i]*0.5f + Vector2{0.5f};
}
return Trade::MeshData2D{MeshPrimitive::TriangleFan, {}, {std::move(positions)}, std::move(textureCoordinates), {}, nullptr};
return Trade::MeshData{MeshPrimitive::TriangleFan, std::move(vertexData), std::move(attributes)};
}
Trade::MeshData2D circle2DWireframe(const UnsignedInt segments) {
Trade::MeshData circle2DWireframe(const UnsignedInt segments) {
CORRADE_ASSERT(segments >= 3, "Primitives::circle2DWireframe(): segments must be >= 3",
(Trade::MeshData2D{MeshPrimitive::LineLoop, {}, {}, {}, {}, nullptr}));
(Trade::MeshData{MeshPrimitive::LineLoop, 0}));
std::vector<Vector2> positions;
positions.reserve(segments);
Containers::Array<char> vertexData{segments*sizeof(Vector2)};
auto positions = Containers::arrayCast<Vector2>(vertexData);
/* Points on circle */
const Rad angleIncrement(Constants::tau()/segments);
for(UnsignedInt i = 0; i != segments; ++i) {
const Rad angle(Float(i)*angleIncrement);
const std::pair<Float, Float> sincos = Math::sincos(angle);
positions.emplace_back(sincos.second, sincos.first);
positions[i] = {sincos.second, sincos.first};
}
return Trade::MeshData2D{MeshPrimitive::LineLoop, {}, {std::move(positions)}, {}, {}, nullptr};
return Trade::MeshData{MeshPrimitive::LineLoop, std::move(vertexData), {Trade::MeshAttributeData{Trade::MeshAttribute::Position, positions}}};
}
Trade::MeshData3D circle3DSolid(const UnsignedInt segments, CircleTextureCoords textureCoords) {
Trade::MeshData circle3DSolid(const UnsignedInt segments, CircleTextureCoords textureCoords) {
CORRADE_ASSERT(segments >= 3, "Primitives::circle3DSolid(): segments must be >= 3",
(Trade::MeshData3D{MeshPrimitive::TriangleFan, {}, {}, {}, {}, {}, nullptr}));
std::vector<Vector3> positions;
positions.reserve(segments + 2);
std::vector<std::vector<Vector2>> textureCoordinates;
if(textureCoords == CircleTextureCoords::Generate)
textureCoordinates.emplace_back();
/* Central point */
positions.emplace_back();
if(textureCoords == CircleTextureCoords::Generate)
textureCoordinates.front().emplace_back(0.5f, 0.5f);
/* Points on circle. The first/last point is here twice to close the circle
properly. */
(Trade::MeshData{MeshPrimitive::TriangleFan, 0}));
/* Allocate interleaved array for all vertex data */
std::size_t stride = 2*sizeof(Vector3);
std::size_t attributeCount = 2;
if(textureCoords == CircleTextureCoords::Generate) {
++attributeCount;
stride += sizeof(Vector2);
}
Containers::Array<char> vertexData{stride*(segments + 2)};
Containers::Array<Trade::MeshAttributeData> attributes{attributeCount};
/* Fill positions */
Containers::StridedArrayView1D<Vector3> positions{vertexData,
reinterpret_cast<Vector3*>(vertexData.begin()),
segments + 2, std::ptrdiff_t(stride)};
attributes[0] =
Trade::MeshAttributeData{Trade::MeshAttribute::Position, positions};
positions[0] = {};
/* Points on the circle. The first/last point is here twice to close the
circle properly. */
const Rad angleIncrement(Constants::tau()/segments);
for(UnsignedInt i = 0; i != segments + 1; ++i) {
const Rad angle(Float(i)*angleIncrement);
const std::pair<Float, Float> sincos = Math::sincos(angle);
Vector3 position{sincos.second, sincos.first, 0.0f};
positions.emplace_back(position);
if(textureCoords == CircleTextureCoords::Generate)
textureCoordinates.front().emplace_back(position.xy()*0.5f + Vector2{0.5f});
positions[i + 1] = {sincos.second, sincos.first, 0.0f};
}
/* Normals. All pointing in the same direction. */
std::vector<Vector3> normals{segments + 2, Vector3::zAxis(1.0f)};
/* Fill normals */
Containers::StridedArrayView1D<Vector3> normals{vertexData,
reinterpret_cast<Vector3*>(vertexData.begin() + sizeof(Vector3)),
segments + 2, std::ptrdiff_t(stride)};
attributes[1] =
Trade::MeshAttributeData{Trade::MeshAttribute::Normal, normals};
for(Vector3& normal: normals) normal = Vector3::zAxis(1.0f);
/* Fill texture coords, if any */
if(textureCoords == CircleTextureCoords::Generate) {
Containers::StridedArrayView1D<Vector2> textureCoords{vertexData,
reinterpret_cast<Vector2*>(vertexData.begin() + 2*sizeof(Vector3)),
positions.size(), std::ptrdiff_t(stride)};
attributes[2] =
Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates, textureCoords};
for(std::size_t i = 0; i != positions.size(); ++i)
textureCoords[i] = positions[i].xy()*0.5f + Vector2{0.5f};
}
return Trade::MeshData3D{MeshPrimitive::TriangleFan, {}, {std::move(positions)}, {std::move(normals)}, std::move(textureCoordinates), {}, nullptr};
return Trade::MeshData{MeshPrimitive::TriangleFan, std::move(vertexData), std::move(attributes)};
}
Trade::MeshData3D circle3DWireframe(const UnsignedInt segments) {
Trade::MeshData circle3DWireframe(const UnsignedInt segments) {
CORRADE_ASSERT(segments >= 3, "Primitives::circle3DWireframe(): segments must be >= 3",
(Trade::MeshData3D{MeshPrimitive::LineLoop, {}, {}, {}, {}, {}, nullptr}));
(Trade::MeshData{MeshPrimitive::LineLoop, 0}));
std::vector<Vector3> positions;
positions.reserve(segments);
Containers::Array<char> vertexData{segments*sizeof(Vector3)};
auto positions = Containers::arrayCast<Vector3>(vertexData);
/* Points on circle */
const Rad angleIncrement(Constants::tau()/segments);
for(UnsignedInt i = 0; i != segments; ++i) {
const Rad angle(Float(i)*angleIncrement);
const std::pair<Float, Float> sincos = Math::sincos(angle);
positions.emplace_back(sincos.second, sincos.first, 0.0f);
positions[i] = {sincos.second, sincos.first, 0.0f};
}
return Trade::MeshData3D{MeshPrimitive::LineLoop, {}, {std::move(positions)}, {}, {}, {}, nullptr};
return Trade::MeshData{MeshPrimitive::LineLoop, std::move(vertexData), {Trade::MeshAttributeData{Trade::MeshAttribute::Position, positions}}};
}
}}

22
src/Magnum/Primitives/Circle.h

@ -51,26 +51,29 @@ enum class CircleTextureCoords: UnsignedByte {
@cpp 3 @ce.
@param textureCoords Whether to generate texture coordinates
Circle with radius @cpp 1.0f @ce. Non-indexed @ref MeshPrimitive::TriangleFan.
Circle with radius @cpp 1.0f @ce. @ref MeshPrimitive::TriangleFan with
@ref MeshIndexType::UnsignedInt indices, interleaved @ref VertexFormat::Vector2
positions and optional @ref VertexFormat::Vector2 texture coordinates.
@image html primitives-circle2dsolid.png width=256px
@see @ref circle2DWireframe(), @ref circle3DSolid()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData2D circle2DSolid(UnsignedInt segments, CircleTextureCoords textureCoords = CircleTextureCoords::DontGenerate);
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData circle2DSolid(UnsignedInt segments, CircleTextureCoords textureCoords = CircleTextureCoords::DontGenerate);
/**
@brief Wireframe 2D circle
@param segments Number of segments. Must be greater or equal to
@cpp 3 @ce.
Circle with radius @cpp 1.0f @ce. Non-indexed @ref MeshPrimitive::LineLoop.
Circle with radius @cpp 1.0f @ce. Non-indexed @ref MeshPrimitive::LineLoop with
@ref VertexFormat::Vector2 positions.
@image html primitives-circle2dwireframe.png width=256px
@see @ref circle2DSolid(), @ref circle3DWireframe()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData2D circle2DWireframe(UnsignedInt segments);
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData circle2DWireframe(UnsignedInt segments);
/**
@brief Solid 3D circle
@ -79,26 +82,27 @@ MAGNUM_PRIMITIVES_EXPORT Trade::MeshData2D circle2DWireframe(UnsignedInt segment
@param textureCoords Whether to generate texture coordinates
Circle on the XY plane with radius @cpp 1.0f @ce. Non-indexed
@ref MeshPrimitive::TriangleFan with normals in positive Z direction.
@ref MeshPrimitive::TriangleFan with interleaved @ref VertexFormat::Vector3
positions, @ref VertexFormat::Vector3 normals in positive Z direction and
optional @ref VertexFormat::Vector2 texture coordinates.
@image html primitives-circle3dsolid.png width=256px
@see @ref circle3DWireframe(), @ref circle2DSolid()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D circle3DSolid(UnsignedInt segments, CircleTextureCoords textureCoords = CircleTextureCoords::DontGenerate);
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData circle3DSolid(UnsignedInt segments, CircleTextureCoords textureCoords = CircleTextureCoords::DontGenerate);
/**
@brief Wireframe 3D circle
@param segments Number of segments. Must be greater or equal to @cpp 3 @ce.
Circle on the XY plane with radius @cpp 1.0f @ce. Non-indexed
@ref MeshPrimitive::LineLoop.
Circle on the XY plane with radius @cpp 1.0f @ce. Non-indexed @ref MeshPrimitive::LineLoop with @ref VertexFormat::Vector2 positions.
@image html primitives-circle3dwireframe.png width=256px
@see @ref circle2DSolid(), @ref circle3DWireframe()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D circle3DWireframe(UnsignedInt segments);
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData circle3DWireframe(UnsignedInt segments);
}}

10
src/Magnum/Primitives/Cone.cpp

@ -29,14 +29,14 @@
#include "Magnum/Math/Color.h"
#include "Magnum/Primitives/Implementation/Spheroid.h"
#include "Magnum/Primitives/Implementation/WireframeSpheroid.h"
#include "Magnum/Trade/MeshData3D.h"
#include "Magnum/Trade/MeshData.h"
namespace Magnum { namespace Primitives {
Trade::MeshData3D coneSolid(const UnsignedInt rings, const UnsignedInt segments, const Float halfLength, const ConeFlags flags) {
Trade::MeshData coneSolid(const UnsignedInt rings, const UnsignedInt segments, const Float halfLength, const ConeFlags flags) {
CORRADE_ASSERT(rings >= 1 && segments >= 3,
"Primitives::coneSolid(): at least one ring and three segments expected",
(Trade::MeshData3D{MeshPrimitive::Triangles, {}, {}, {}, {}, {}, nullptr}));
(Trade::MeshData{MeshPrimitive::Triangles, 0}));
Implementation::Spheroid cone{segments, flags & ConeFlag::GenerateTextureCoords ? Implementation::Spheroid::TextureCoords::Generate : Implementation::Spheroid::TextureCoords::DontGenerate};
@ -64,10 +64,10 @@ Trade::MeshData3D coneSolid(const UnsignedInt rings, const UnsignedInt segments,
return cone.finalize();
}
Trade::MeshData3D coneWireframe(const UnsignedInt segments, const Float halfLength) {
Trade::MeshData coneWireframe(const UnsignedInt segments, const Float halfLength) {
CORRADE_ASSERT(segments >= 4 && segments%4 == 0,
"Primitives::coneWireframe(): multiples of 4 segments expected",
(Trade::MeshData3D{MeshPrimitive::Lines, {}, {}, {}, {}, {}, nullptr}));
(Trade::MeshData{MeshPrimitive::Lines, 0}));
Implementation::WireframeSpheroid cone{segments/4};
cone.ring(-halfLength);

23
src/Magnum/Primitives/Cone.h

@ -65,21 +65,22 @@ CORRADE_ENUMSET_OPERATORS(ConeFlags)
@param halfLength Half the cone length
@param flags Flags
Cone along Y axis of radius @cpp 1.0f @ce. Indexed
@ref MeshPrimitive::Triangles. 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.
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.
@image html primitives-conesolid.png width=256px
The cone is by default created with radius set to @f$ 1.0 @f$. In order to get
radius @f$ r @f$, length @f$ l @f$ and preserve correct normals, set
@p halfLength to @f$ 0.5 \frac{l}{r} @f$ and then scale all
@ref Trade::MeshData3D::positions() by @f$ r @f$, for example using
@ref MeshTools::transformPointsInPlace().
@p halfLength to @f$ 0.5 \frac{l}{r} @f$ and then scale all positions by
@f$ r @f$, for example using @ref MeshTools::transformPointsInPlace().
@see @ref coneWireframe(), @ref cylinderSolid()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D coneSolid(UnsignedInt rings, UnsignedInt segments, Float halfLength, ConeFlags flags = {});
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData coneSolid(UnsignedInt rings, UnsignedInt segments, Float halfLength, ConeFlags flags = {});
/**
@brief Wireframe 3D cone
@ -87,13 +88,15 @@ MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D coneSolid(UnsignedInt rings, Unsigned
@cpp 4 @ce and multiple of @cpp 4 @ce.
@param halfLength Half the cone length
Cone along Y axis of radius @cpp 1.0f @ce. Indexed @ref MeshPrimitive::Lines.
Cone along Y axis of radius @cpp 1.0f @ce. @ref MeshPrimitive::Lines with
@ref MeshIndexType::UnsignedInt indices and @ref VertexFormat::Vector3
positions.
@image html primitives-conewireframe.png width=256px
@see @ref coneSolid(), @ref cylinderWireframe()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D coneWireframe(UnsignedInt segments, Float halfLength);
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData coneWireframe(UnsignedInt segments, Float halfLength);
}}

41
src/Magnum/Primitives/Crosshair.cpp

@ -27,24 +27,39 @@
#include "Magnum/Mesh.h"
#include "Magnum/Math/Color.h"
#include "Magnum/Trade/MeshData2D.h"
#include "Magnum/Trade/MeshData3D.h"
#include "Magnum/Trade/MeshData.h"
namespace Magnum { namespace Primitives {
Trade::MeshData2D crosshair2D() {
return Trade::MeshData2D{MeshPrimitive::Lines, {}, {{
{-1.0f, 0.0f}, {1.0f, 0.0f},
{ 0.0f, -1.0f}, {0.0f, 1.0f}
}}, {}, {}, nullptr};
namespace {
constexpr Vector2 Positions2D[]{
{-1.0f, 0.0f}, {1.0f, 0.0f},
{ 0.0f, -1.0f}, {0.0f, 1.0f}
};
constexpr Vector3 Positions3D[]{
{-1.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 0.0f},
{ 0.0f, -1.0f, 0.0f}, {0.0f, 1.0f, 0.0f},
{ 0.0f, 0.0f, -1.0f}, {0.0f, 0.0f, 1.0f}
};
constexpr Trade::MeshAttributeData Attributes2D[]{
Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::arrayView(Positions2D)}
};
constexpr Trade::MeshAttributeData Attributes3D[]{
Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::arrayView(Positions3D)}
};
}
Trade::MeshData crosshair2D() {
return Trade::MeshData{MeshPrimitive::Lines, {}, Positions2D,
Trade::meshAttributeDataNonOwningArray(Attributes2D)};
}
Trade::MeshData3D crosshair3D() {
return Trade::MeshData3D{MeshPrimitive::Lines, {}, {{
{-1.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 0.0f},
{ 0.0f, -1.0f, 0.0f}, {0.0f, 1.0f, 0.0f},
{ 0.0f, 0.0f, -1.0f}, {0.0f, 0.0f, 1.0f}
}}, {}, {}, {}, nullptr};
Trade::MeshData crosshair3D() {
return Trade::MeshData{MeshPrimitive::Lines, {}, Positions3D,
Trade::meshAttributeDataNonOwningArray(Attributes3D)};
}
}}

12
src/Magnum/Primitives/Crosshair.h

@ -37,24 +37,28 @@ namespace Magnum { namespace Primitives {
/**
@brief 2D crosshair
2x2 crosshair (two crossed lines), non-indexed @ref MeshPrimitive::Lines.
2x2 crosshair (two crossed lines). Non-indexed @ref MeshPrimitive::Lines with
@ref VertexFormat::Vector2 positions. The returned instance references data
stored in constant memory.
@image html primitives-crosshair2d.png width=256px
@see @ref crosshair3D(), @ref axis2D(), @ref line2D()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData2D crosshair2D();
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData crosshair2D();
/**
@brief 3D crosshair
2x2x2 crosshair (three crossed lines), non-indexed @ref MeshPrimitive::Lines.
2x2x2 crosshair (three crossed lines). Non-indexed @ref MeshPrimitive::Lines
with @ref VertexFormat::Vector3 positions. The returned instance references
data stored in constant memory.
@image html primitives-crosshair3d.png width=256px
@see @ref crosshair2D(), @ref axis2D(), @ref line3D()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D crosshair3D();
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData crosshair3D();
}}

224
src/Magnum/Primitives/Cube.cpp

@ -27,82 +27,75 @@
#include "Magnum/Mesh.h"
#include "Magnum/Math/Color.h"
#include "Magnum/Trade/MeshData3D.h"
#include "Magnum/Trade/MeshData.h"
namespace Magnum { namespace Primitives {
Trade::MeshData3D cubeSolid() {
return Trade::MeshData3D{MeshPrimitive::Triangles, {
0, 1, 2, 0, 2, 3, /* +Z */
4, 5, 6, 4, 6, 7, /* +X */
8, 9, 10, 8, 10, 11, /* +Y */
12, 13, 14, 12, 14, 15, /* -Z */
16, 17, 18, 16, 18, 19, /* -Y */
20, 21, 22, 20, 22, 23 /* -X */
}, {{
{-1.0f, -1.0f, 1.0f},
{ 1.0f, -1.0f, 1.0f},
{ 1.0f, 1.0f, 1.0f}, /* +Z */
{-1.0f, 1.0f, 1.0f},
{ 1.0f, -1.0f, 1.0f},
{ 1.0f, -1.0f, -1.0f},
{ 1.0f, 1.0f, -1.0f}, /* +X */
{ 1.0f, 1.0f, 1.0f},
{-1.0f, 1.0f, 1.0f},
{ 1.0f, 1.0f, 1.0f},
{ 1.0f, 1.0f, -1.0f}, /* +Y */
{-1.0f, 1.0f, -1.0f},
{ 1.0f, -1.0f, -1.0f},
{-1.0f, -1.0f, -1.0f},
{-1.0f, 1.0f, -1.0f}, /* -Z */
{ 1.0f, 1.0f, -1.0f},
{-1.0f, -1.0f, -1.0f},
{ 1.0f, -1.0f, -1.0f},
{ 1.0f, -1.0f, 1.0f}, /* -Y */
{-1.0f, -1.0f, 1.0f},
{-1.0f, -1.0f, -1.0f},
{-1.0f, -1.0f, 1.0f},
{-1.0f, 1.0f, 1.0f}, /* -X */
{-1.0f, 1.0f, -1.0f}
}}, {{
{ 0.0f, 0.0f, 1.0f},
{ 0.0f, 0.0f, 1.0f},
{ 0.0f, 0.0f, 1.0f}, /* +Z */
{ 0.0f, 0.0f, 1.0f},
{ 1.0f, 0.0f, 0.0f},
{ 1.0f, 0.0f, 0.0f},
{ 1.0f, 0.0f, 0.0f}, /* +X */
{ 1.0f, 0.0f, 0.0f},
{ 0.0f, 1.0f, 0.0f},
{ 0.0f, 1.0f, 0.0f},
{ 0.0f, 1.0f, 0.0f}, /* +Y */
{ 0.0f, 1.0f, 0.0f},
{ 0.0f, 0.0f, -1.0f},
{ 0.0f, 0.0f, -1.0f},
{ 0.0f, 0.0f, -1.0f}, /* -Z */
{ 0.0f, 0.0f, -1.0f},
{ 0.0f, -1.0f, 0.0f},
{ 0.0f, -1.0f, 0.0f},
{ 0.0f, -1.0f, 0.0f}, /* -Y */
{ 0.0f, -1.0f, 0.0f},
{-1.0f, 0.0f, 0.0f},
{-1.0f, 0.0f, 0.0f},
{-1.0f, 0.0f, 0.0f}, /* -X */
{-1.0f, 0.0f, 0.0f}
}}, {}, {}, nullptr};
namespace {
/* not 8-bit because GPUs (and Vulkan) don't like it nowadays */
constexpr UnsignedShort IndicesSolid[]{
0, 1, 2, 0, 2, 3, /* +Z */
4, 5, 6, 4, 6, 7, /* +X */
8, 9, 10, 8, 10, 11, /* +Y */
12, 13, 14, 12, 14, 15, /* -Z */
16, 17, 18, 16, 18, 19, /* -Y */
20, 21, 22, 20, 22, 23 /* -X */
};
constexpr struct VertexSolid {
Vector3 position;
Vector3 normal;
} VerticesSolid[]{
{{-1.0f, -1.0f, 1.0f}, { 0.0f, 0.0f, 1.0f}},
{{ 1.0f, -1.0f, 1.0f}, { 0.0f, 0.0f, 1.0f}},
{{ 1.0f, 1.0f, 1.0f}, { 0.0f, 0.0f, 1.0f}}, /* +Z */
{{-1.0f, 1.0f, 1.0f}, { 0.0f, 0.0f, 1.0f}},
{{ 1.0f, -1.0f, 1.0f}, { 1.0f, 0.0f, 0.0f}},
{{ 1.0f, -1.0f, -1.0f}, { 1.0f, 0.0f, 0.0f}},
{{ 1.0f, 1.0f, -1.0f}, { 1.0f, 0.0f, 0.0f}}, /* +X */
{{ 1.0f, 1.0f, 1.0f}, { 1.0f, 0.0f, 0.0f}},
{{-1.0f, 1.0f, 1.0f}, { 0.0f, 1.0f, 0.0f}},
{{ 1.0f, 1.0f, 1.0f}, { 0.0f, 1.0f, 0.0f}},
{{ 1.0f, 1.0f, -1.0f}, { 0.0f, 1.0f, 0.0f}}, /* +Y */
{{-1.0f, 1.0f, -1.0f}, { 0.0f, 1.0f, 0.0f}},
{{ 1.0f, -1.0f, -1.0f}, { 0.0f, 0.0f, -1.0f}},
{{-1.0f, -1.0f, -1.0f}, { 0.0f, 0.0f, -1.0f}},
{{-1.0f, 1.0f, -1.0f}, { 0.0f, 0.0f, -1.0f}}, /* -Z */
{{ 1.0f, 1.0f, -1.0f}, { 0.0f, 0.0f, -1.0f}},
{{-1.0f, -1.0f, -1.0f}, { 0.0f, -1.0f, 0.0f}},
{{ 1.0f, -1.0f, -1.0f}, { 0.0f, -1.0f, 0.0f}},
{{ 1.0f, -1.0f, 1.0f}, { 0.0f, -1.0f, 0.0f}}, /* -Y */
{{-1.0f, -1.0f, 1.0f}, { 0.0f, -1.0f, 0.0f}},
{{-1.0f, -1.0f, -1.0f}, {-1.0f, 0.0f, 0.0f}},
{{-1.0f, -1.0f, 1.0f}, {-1.0f, 0.0f, 0.0f}},
{{-1.0f, 1.0f, 1.0f}, {-1.0f, 0.0f, 0.0f}}, /* -X */
{{-1.0f, 1.0f, -1.0f}, {-1.0f, 0.0f, 0.0f}}
};
constexpr Trade::MeshAttributeData AttributesSolid[]{
Trade::MeshAttributeData{Trade::MeshAttribute::Position,
Containers::stridedArrayView(VerticesSolid, &VerticesSolid[0].position,
Containers::arraySize(VerticesSolid), sizeof(VertexSolid))},
Trade::MeshAttributeData{Trade::MeshAttribute::Normal,
Containers::stridedArrayView(VerticesSolid, &VerticesSolid[0].normal,
Containers::arraySize(VerticesSolid), sizeof(VertexSolid))}
};
}
Trade::MeshData cubeSolid() {
return Trade::MeshData{MeshPrimitive::Triangles,
{}, IndicesSolid, Trade::MeshIndexData{IndicesSolid},
{}, VerticesSolid, Trade::meshAttributeDataNonOwningArray(AttributesSolid)};
}
Trade::MeshData3D cubeSolidStrip() {
namespace {
constexpr Vector3 VerticesSolidStrip[]{
/* Sources:
https://twitter.com/Donzanoid/status/436843034966507520
http://www.asmcommunity.net/forums/topic/?id=6284#post-45209
@ -126,41 +119,64 @@ Trade::MeshData3D cubeSolidStrip() {
|F \|
2---3
*/
return Trade::MeshData3D{MeshPrimitive::TriangleStrip, {}, {{
{ 1.0f, 1.0f, 1.0f}, /* 3 */
{-1.0f, 1.0f, 1.0f}, /* 2 */
{ 1.0f, -1.0f, 1.0f}, /* 6 */
{-1.0f, -1.0f, 1.0f}, /* 7 */
{-1.0f, -1.0f, -1.0f}, /* 4 */
{-1.0f, 1.0f, 1.0f}, /* 2 */
{-1.0f, 1.0f, -1.0f}, /* 0 */
{ 1.0f, 1.0f, 1.0f}, /* 3 */
{ 1.0f, 1.0f, -1.0f}, /* 1 */
{ 1.0f, -1.0f, 1.0f}, /* 6 */
{ 1.0f, -1.0f, -1.0f}, /* 5 */
{-1.0f, -1.0f, -1.0f}, /* 4 */
{ 1.0f, 1.0f, -1.0f}, /* 1 */
{-1.0f, 1.0f, -1.0f} /* 0 */
}}, {}, {}, {}, nullptr};
{ 1.0f, 1.0f, 1.0f}, /* 3 */
{-1.0f, 1.0f, 1.0f}, /* 2 */
{ 1.0f, -1.0f, 1.0f}, /* 6 */
{-1.0f, -1.0f, 1.0f}, /* 7 */
{-1.0f, -1.0f, -1.0f}, /* 4 */
{-1.0f, 1.0f, 1.0f}, /* 2 */
{-1.0f, 1.0f, -1.0f}, /* 0 */
{ 1.0f, 1.0f, 1.0f}, /* 3 */
{ 1.0f, 1.0f, -1.0f}, /* 1 */
{ 1.0f, -1.0f, 1.0f}, /* 6 */
{ 1.0f, -1.0f, -1.0f}, /* 5 */
{-1.0f, -1.0f, -1.0f}, /* 4 */
{ 1.0f, 1.0f, -1.0f}, /* 1 */
{-1.0f, 1.0f, -1.0f} /* 0 */
};
constexpr Trade::MeshAttributeData AttributesSolidStrip[]{
Trade::MeshAttributeData{Trade::MeshAttribute::Position,
Containers::stridedArrayView(VerticesSolidStrip)}
};
}
Trade::MeshData cubeSolidStrip() {
return Trade::MeshData{MeshPrimitive::TriangleStrip,
{}, VerticesSolidStrip, Trade::meshAttributeDataNonOwningArray(AttributesSolidStrip)};
}
namespace {
/* not 8-bit because GPUs (and Vulkan) don't like it nowadays */
constexpr UnsignedShort IndicesWireframe[]{
0, 1, 1, 2, 2, 3, 3, 0, /* +Z */
4, 5, 5, 6, 6, 7, 7, 4, /* -Z */
1, 5, 2, 6, /* +X */
0, 4, 3, 7 /* -X */
};
constexpr Vector3 VerticesWireframe[]{
{-1.0f, -1.0f, 1.0f},
{ 1.0f, -1.0f, 1.0f},
{ 1.0f, 1.0f, 1.0f},
{-1.0f, 1.0f, 1.0f},
{-1.0f, -1.0f, -1.0f},
{ 1.0f, -1.0f, -1.0f},
{ 1.0f, 1.0f, -1.0f},
{-1.0f, 1.0f, -1.0f}
};
constexpr Trade::MeshAttributeData AttributesWireframe[]{
Trade::MeshAttributeData{Trade::MeshAttribute::Position,
Containers::stridedArrayView(VerticesWireframe)}
};
}
Trade::MeshData3D cubeWireframe() {
return Trade::MeshData3D{MeshPrimitive::Lines, {
0, 1, 1, 2, 2, 3, 3, 0, /* +Z */
4, 5, 5, 6, 6, 7, 7, 4, /* -Z */
1, 5, 2, 6, /* +X */
0, 4, 3, 7 /* -X */
}, {{
{-1.0f, -1.0f, 1.0f},
{ 1.0f, -1.0f, 1.0f},
{ 1.0f, 1.0f, 1.0f},
{-1.0f, 1.0f, 1.0f},
{-1.0f, -1.0f, -1.0f},
{ 1.0f, -1.0f, -1.0f},
{ 1.0f, 1.0f, -1.0f},
{-1.0f, 1.0f, -1.0f}
}}, {}, {}, {}, nullptr};
Trade::MeshData cubeWireframe() {
return Trade::MeshData{MeshPrimitive::Lines,
{}, IndicesWireframe, Trade::MeshIndexData{IndicesWireframe},
{}, VerticesWireframe, Trade::meshAttributeDataNonOwningArray(AttributesWireframe)};
}
}}

22
src/Magnum/Primitives/Cube.h

@ -37,36 +37,42 @@ namespace Magnum { namespace Primitives {
/**
@brief Solid 3D cube
Indexed @ref MeshPrimitive::Triangles with flat normals.
@ref MeshPrimitive::Triangles with @ref MeshIndexType::UnsignedShort indices,
interleaved @ref VertexFormat::Vector3 positions and flat
@ref VertexFormat::Vector3 normals. The returned instance references data
stored in constant memory.
@image html primitives-cubesolid.png width=256px
@see @ref cubeSolidStrip(), @ref cubeWireframe()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D cubeSolid();
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData cubeSolid();
/**
@brief Solid 3D cube as a single strip
Non-indexed @ref MeshPrimitive::TriangleStrip. Just positions, no
normals or anything else.
Non-indexed @ref MeshPrimitive::TriangleStrip with @ref VertexFormat::Vector3
positions. The returned instance references data stored in constant memory. No
normals or anything else --- use @ref cubeSolid() instead if you need these.
@image html primitives-cubesolid.png width=256px
@see @ref cubeSolid(), @ref cubeWireframe()
@see @ref cubeWireframe()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D cubeSolidStrip();
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData cubeSolidStrip();
/**
@brief Wireframe 3D cube
Indexed @ref MeshPrimitive::Lines.
@ref MeshPrimitive::Lines with @ref MeshIndexType::UnsignedShort indices and
@ref VertexFormat::Vector3 positions. The returned instance references data
stored in constant memory.
@image html primitives-cubewireframe.png width=256px
@see @ref cubeSolid(), @ref cubeSolidStrip()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D cubeWireframe();
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData cubeWireframe();
}}

10
src/Magnum/Primitives/Cylinder.cpp

@ -29,14 +29,14 @@
#include "Magnum/Math/Color.h"
#include "Magnum/Primitives/Implementation/Spheroid.h"
#include "Magnum/Primitives/Implementation/WireframeSpheroid.h"
#include "Magnum/Trade/MeshData3D.h"
#include "Magnum/Trade/MeshData.h"
namespace Magnum { namespace Primitives {
Trade::MeshData3D cylinderSolid(const UnsignedInt rings, const UnsignedInt segments, const Float halfLength, const CylinderFlags flags) {
Trade::MeshData cylinderSolid(const UnsignedInt rings, const UnsignedInt segments, const Float halfLength, const CylinderFlags flags) {
CORRADE_ASSERT(rings >= 1 && segments >= 3,
"Primitives::cylinderSolid(): at least one ring and three segments expected",
(Trade::MeshData3D{MeshPrimitive::Triangles, {}, {}, {}, {}, {}, nullptr}));
(Trade::MeshData{MeshPrimitive::Triangles, 0}));
Implementation::Spheroid cylinder(segments, flags & CylinderFlag::GenerateTextureCoords ? Implementation::Spheroid::TextureCoords::Generate : Implementation::Spheroid::TextureCoords::DontGenerate);
@ -71,10 +71,10 @@ Trade::MeshData3D cylinderSolid(const UnsignedInt rings, const UnsignedInt segme
return cylinder.finalize();
}
Trade::MeshData3D cylinderWireframe(const UnsignedInt rings, const UnsignedInt segments, const Float halfLength) {
Trade::MeshData cylinderWireframe(const UnsignedInt rings, const UnsignedInt segments, const Float halfLength) {
CORRADE_ASSERT(rings >= 1 && segments >= 4 && segments%4 == 0,
"Primitives::cylinderWireframe(): at least one ring and multiples of 4 segments expected",
(Trade::MeshData3D{MeshPrimitive::Lines, {}, {}, {}, {}, {}, nullptr}));
(Trade::MeshData{MeshPrimitive::Lines, 0}));
Implementation::WireframeSpheroid cylinder(segments/4);

24
src/Magnum/Primitives/Cylinder.h

@ -65,21 +65,22 @@ CORRADE_ENUMSET_OPERATORS(CylinderFlags)
@param halfLength Half the cylinder length
@param flags Flags
Cylinder along Y axis of radius @cpp 1.0f @ce. Indexed
@ref MeshPrimitive::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.
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.
@image html primitives-cylindersolid.png width=256px
The cylinder is by default created with radius set to @f$ 1.0 @f$. In order to
get radius @f$ r @f$, length @f$ l @f$ and preserve correct normals, set
@p halfLength to @f$ 0.5 \frac{l}{r} @f$ and then scale all
@ref Trade::MeshData3D::positions() by @f$ r @f$, for example using
@ref MeshTools::transformPointsInPlace().
@p halfLength to @f$ 0.5 \frac{l}{r} @f$ and then scale all positions by
@f$ r @f$, for example using @ref MeshTools::transformPointsInPlace().
@see @ref cylinderWireframe(), @ref coneSolid()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D cylinderSolid(UnsignedInt rings, UnsignedInt segments, Float halfLength, CylinderFlags flags = {});
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData cylinderSolid(UnsignedInt rings, UnsignedInt segments, Float halfLength, CylinderFlags flags = {});
/**
@brief Wireframe 3D cylinder
@ -89,14 +90,15 @@ MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D cylinderSolid(UnsignedInt rings, Unsi
@cpp 4 @ce and multiple of @cpp 4 @ce.
@param halfLength Half the cylinder length
Cylinder along Y axis of radius @cpp 1.0f @ce. Indexed
@ref MeshPrimitive::Lines.
Cylinder along Y axis of radius @cpp 1.0f @ce. @ref MeshPrimitive::Lines with
@ref MeshIndexType::UnsignedInt indices and @ref VertexFormat::Vector3
positions.
@image html primitives-cylinderwireframe.png width=256px
@see @ref cylinderSolid(), @ref coneWireframe()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D cylinderWireframe(UnsignedInt rings, UnsignedInt segments, Float halfLength);
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData cylinderWireframe(UnsignedInt rings, UnsignedInt segments, Float halfLength);
}}

86
src/Magnum/Primitives/Gradient.cpp

@ -28,17 +28,22 @@
#include "Magnum/Mesh.h"
#include "Magnum/Math/Color.h"
#include "Magnum/Math/Intersection.h"
#include "Magnum/Trade/MeshData2D.h"
#include "Magnum/Trade/MeshData3D.h"
#include "Magnum/Trade/MeshData.h"
namespace Magnum { namespace Primitives {
Trade::MeshData2D gradient2D(const Vector2& a, const Color4& colorA, const Vector2& b, const Color4& colorB) {
std::vector<Vector2> positions{Vector2{ 1.0f, -1.0f},
Vector2{ 1.0f, 1.0f},
Vector2{-1.0f, -1.0f},
Vector2{-1.0f, 1.0f}};
std::vector<Color4> colors{4};
Trade::MeshData gradient2D(const Vector2& a, const Color4& colorA, const Vector2& b, const Color4& colorB) {
struct Vertex {
Vector2 position;
Color4 color;
};
Containers::Array<char> vertexData{sizeof(Vertex)*4};
auto vertices = Containers::arrayCast<Vertex>(vertexData);
vertices[0].position = { 1.0f, -1.0f};
vertices[1].position = { 1.0f, 1.0f};
vertices[2].position = {-1.0f, -1.0f};
vertices[3].position = {-1.0f, 1.0f};
/* For every corner, take a line perpendicular to the gradient direction
and passing through the corner. The calculated intersection position
@ -47,27 +52,45 @@ Trade::MeshData2D gradient2D(const Vector2& a, const Color4& colorA, const Vecto
const Vector2 direction = b - a;
const Vector2 perpendicular = direction.perpendicular();
for(std::size_t i = 0; i != 4; ++i) {
const Float t = Math::Intersection::lineSegmentLine(a, direction, positions[i], perpendicular);
colors[i] = Math::lerp(colorA, colorB, t);
const Float t = Math::Intersection::lineSegmentLine(a, direction, vertices[i].position, perpendicular);
vertices[i].color = Math::lerp(colorA, colorB, t);
}
return Trade::MeshData2D{MeshPrimitive::TriangleStrip, {}, {std::move(positions)}, {}, {std::move(colors)}, nullptr};
Trade::MeshAttributeData positions{Trade::MeshAttribute::Position,
Containers::stridedArrayView(vertices, &vertices[0].position,
vertices.size(), sizeof(Vertex))};
Trade::MeshAttributeData colors{Trade::MeshAttribute::Color,
Containers::stridedArrayView(vertices, &vertices[0].color,
vertices.size(), sizeof(Vertex))};
return Trade::MeshData{MeshPrimitive::TriangleStrip,
std::move(vertexData), {positions, colors}};
}
Trade::MeshData2D gradient2DHorizontal(const Color4& colorLeft, const Color4& colorRight) {
Trade::MeshData gradient2DHorizontal(const Color4& colorLeft, const Color4& colorRight) {
return Primitives::gradient2D({-1.0f, 0.0f}, colorLeft, {1.0f, 0.0f}, colorRight);
}
Trade::MeshData2D gradient2DVertical(const Color4& colorBottom, const Color4& colorTop) {
Trade::MeshData gradient2DVertical(const Color4& colorBottom, const Color4& colorTop) {
return Primitives::gradient2D({0.0f, -1.0f}, colorBottom, {0.0f, 1.0f}, colorTop);
}
Trade::MeshData3D gradient3D(const Vector3& a, const Color4& colorA, const Vector3& b, const Color4& colorB) {
std::vector<Vector3> positions{Vector3{ 1.0f, -1.0f, 0.0f},
Vector3{ 1.0f, 1.0f, 0.0f},
Vector3{-1.0f, -1.0f, 0.0f},
Vector3{-1.0f, 1.0f, 0.0f}};
std::vector<Color4> colors{4};
Trade::MeshData gradient3D(const Vector3& a, const Color4& colorA, const Vector3& b, const Color4& colorB) {
struct Vertex {
Vector3 position;
Vector3 normal;
Color4 color;
};
Containers::Array<char> vertexData{sizeof(Vertex)*4};
auto vertices = Containers::arrayCast<Vertex>(vertexData);
vertices[0].position = { 1.0f, -1.0f, 0};
vertices[1].position = { 1.0f, 1.0f, 0};
vertices[2].position = {-1.0f, -1.0f, 0};
vertices[3].position = {-1.0f, 1.0f, 0};
vertices[0].normal = {0.0f, 0.0f, 1.0f};
vertices[1].normal = {0.0f, 0.0f, 1.0f};
vertices[2].normal = {0.0f, 0.0f, 1.0f};
vertices[3].normal = {0.0f, 0.0f, 1.0f};
/* For every corner, take a plane perpendicular to the gradient direction
and passing through the corner. The calculated intersection position
@ -75,24 +98,29 @@ Trade::MeshData3D gradient3D(const Vector3& a, const Color4& colorA, const Vecto
for given corner. */
const Vector3 direction = b - a;
for(std::size_t i = 0; i != 4; ++i) {
const Vector4 plane = Math::planeEquation(direction, positions[i]);
const Vector4 plane = Math::planeEquation(direction, vertices[i].position);
const Float t = Math::Intersection::planeLine(plane, a, direction);
colors[i] = Math::lerp(colorA, colorB, t);
vertices[i].color = Math::lerp(colorA, colorB, t);
}
return Trade::MeshData3D{MeshPrimitive::TriangleStrip, {}, {std::move(positions)}, {{
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f}
}}, {}, {std::move(colors)}, nullptr};
Trade::MeshAttributeData positions{Trade::MeshAttribute::Position,
Containers::stridedArrayView(vertices, &vertices[0].position,
vertices.size(), sizeof(Vertex))};
Trade::MeshAttributeData normals{Trade::MeshAttribute::Normal,
Containers::stridedArrayView(vertices, &vertices[0].normal,
vertices.size(), sizeof(Vertex))};
Trade::MeshAttributeData colors{Trade::MeshAttribute::Color,
Containers::stridedArrayView(vertices, &vertices[0].color,
vertices.size(), sizeof(Vertex))};
return Trade::MeshData{MeshPrimitive::TriangleStrip,
std::move(vertexData), {positions, normals, colors}};
}
Trade::MeshData3D gradient3DHorizontal(const Color4& colorLeft, const Color4& colorRight) {
Trade::MeshData gradient3DHorizontal(const Color4& colorLeft, const Color4& colorRight) {
return Primitives::gradient3D({-1.0f, 0.0f, 0.0f}, colorLeft, {1.0f, 0.0f, 0.0f}, colorRight);
}
Trade::MeshData3D gradient3DVertical(const Color4& colorBottom, const Color4& colorTop) {
Trade::MeshData gradient3DVertical(const Color4& colorBottom, const Color4& colorTop) {
return Primitives::gradient3D({0.0f, -1.0f, 0.0f}, colorBottom, {0.0f, 1.0f, 0.0f}, colorTop);
}

28
src/Magnum/Primitives/Gradient.h

@ -38,16 +38,18 @@ namespace Magnum { namespace Primitives {
/**
@brief 2D square with a gradient
2x2 square with vertex colors. Non-indexed @ref MeshPrimitive::TriangleStrip.
Vertex colors correspond to the gradient defined by the endpoints @p a and
@p b, linearly interpolated from @p colorA to @p colorB.
2x2 square with vertex colors. Non-indexed @ref MeshPrimitive::TriangleStrip
with interleaved @ref VertexFormat::Vector2 positions and
@ref VertexFormat::Vector4 colors. Vertex colors correspond to the gradient
defined by the endpoints @p a and @p b, linearly interpolated from @p colorA to
@p colorB.
@image html primitives-gradient2d.png width=256px
@see @ref gradient2DHorizontal(), @ref gradient2DVertical(), @ref gradient3D(),
@ref squareSolid()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData2D gradient2D(const Vector2& a, const Color4& colorA, const Vector2& b, const Color4& colorB);
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData gradient2D(const Vector2& a, const Color4& colorA, const Vector2& b, const Color4& colorB);
/**
@brief 2D square with a horizontal gradient
@ -60,7 +62,7 @@ Equivalent to calling @ref gradient2D() like this:
@see @ref gradient2DVertical(), @ref gradient3DHorizontal(), @ref squareSolid()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData2D gradient2DHorizontal(const Color4& colorLeft, const Color4& colorRight);
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData gradient2DHorizontal(const Color4& colorLeft, const Color4& colorRight);
/**
@brief 2D square with a vertical gradient
@ -73,22 +75,24 @@ Equivalent to calling @ref gradient2D() like this:
@see @ref gradient2DHorizontal(), @ref gradient3DVertical(), @ref squareSolid()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData2D gradient2DVertical(const Color4& colorBottom, const Color4& colorTop);
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData gradient2DVertical(const Color4& colorBottom, const Color4& colorTop);
/**
@brief 3D plane with a gradient
2x2 plane with vertex colors. Non-indexed @ref MeshPrimitive::TriangleStrip on
the XY plane with normals in positive Z direction. Vertex colors correspond to
the gradient defined by the endpoints @p a and @p b, linearly interpolated from
@p colorA to @p colorB.
the XY plane with interleaved @ref VertexFormat::Vector3 positions,
@ref VertexFormat::Vector3 normals in positive Z direction and
@ref VertexFormat::Vector4 colors. Vertex colors correspond to the gradient
defined by the endpoints @p a and @p b, linearly interpolated from @p colorA to
@p colorB.
@image html primitives-gradient3d.png width=256px
@see @ref gradient3DHorizontal(), @ref gradient3DVertical(), @ref gradient2D(),
@ref planeSolid()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D gradient3D(const Vector3& a, const Color4& colorA, const Vector3& b, const Color4& colorB);
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData gradient3D(const Vector3& a, const Color4& colorA, const Vector3& b, const Color4& colorB);
/**
@brief 3D plane with a horizontal gradient
@ -101,7 +105,7 @@ Equivalent to calling @ref gradient3D() like this:
@see @ref gradient3DVertical(), @ref gradient2DHorizontal(), @ref planeSolid()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D gradient3DHorizontal(const Color4& colorLeft, const Color4& colorRight);
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData gradient3DHorizontal(const Color4& colorLeft, const Color4& colorRight);
/**
@brief 3D plane with a vertical gradient
@ -114,7 +118,7 @@ Equivalent to calling @ref gradient3D() like this:
@see @ref gradient3DHorizontal(), @ref gradient2DVertical(), @ref planeSolid()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D gradient3DVertical(const Color4& colorBottom, const Color4& colorTop);
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData gradient3DVertical(const Color4& colorBottom, const Color4& colorTop);
}}

159
src/Magnum/Primitives/Grid.cpp

@ -27,82 +27,133 @@
#include "Magnum/Mesh.h"
#include "Magnum/Math/Color.h"
#include "Magnum/Trade/MeshData3D.h"
#include "Magnum/Trade/MeshData.h"
namespace Magnum { namespace Primitives {
Trade::MeshData3D grid3DSolid(const Vector2i& subdivisions, const GridFlags flags) {
Trade::MeshData grid3DSolid(const Vector2i& subdivisions, const GridFlags flags) {
const Vector2i vertexCount = subdivisions + Vector2i{2};
const Vector2i faceCount = subdivisions + Vector2i{1};
std::vector<Vector3> positions;
positions.reserve(vertexCount.product());
for(Int y = 0; y != vertexCount.y(); ++y)
for(Int x = 0; x != vertexCount.x(); ++x)
positions.emplace_back((Vector2(x, y)/Vector2(faceCount))*2.0f - Vector2{1.0f}, 0.0f);
std::vector<UnsignedInt> indices;
indices.reserve(faceCount.product()*6);
for(Int y = 0; y != faceCount.y(); ++y) {
for(Int x = 0; x != faceCount.x(); ++x) {
/* 2--1 5
| / /|
|/ / |
0 3--4 */
indices.insert(indices.end(), {
UnsignedInt(y*vertexCount.x() + x),
UnsignedInt((y + 1)*vertexCount.x() + x + 1),
UnsignedInt((y + 1)*vertexCount.x() + x + 0),
UnsignedInt(y*vertexCount.x() + x),
UnsignedInt(y*vertexCount.x() + x + 1),
UnsignedInt((y + 1)*vertexCount.x() + x + 1)});
/* Indices */
Containers::Array<char> indexData{std::size_t(faceCount.product()*6)*sizeof(UnsignedInt)};
auto indices = Containers::arrayCast<UnsignedInt>(indexData);
{
std::size_t i = 0;
for(Int y = 0; y != faceCount.y(); ++y) {
for(Int x = 0; x != faceCount.x(); ++x) {
/* 2--1 5
| / /|
|/ / |
0 3--4 */
indices[i++] = UnsignedInt(y*vertexCount.x() + x);
indices[i++] = UnsignedInt((y + 1)*vertexCount.x() + x + 1);
indices[i++] = UnsignedInt((y + 1)*vertexCount.x() + x + 0);
indices[i++] = UnsignedInt(y*vertexCount.x() + x);
indices[i++] = UnsignedInt(y*vertexCount.x() + x + 1);
indices[i++] = UnsignedInt((y + 1)*vertexCount.x() + x + 1);
}
}
}
std::vector<std::vector<Vector3>> normals;
if(flags & GridFlag::GenerateNormals)
normals.emplace_back(positions.size(), Vector3::zAxis(1.0f));
/* Allocate interleaved array for all vertex data */
std::size_t stride = sizeof(Vector3);
std::size_t attributeCount = 1;
if(flags & GridFlag::GenerateNormals) {
++attributeCount;
stride += sizeof(Vector3);
}
if(flags & GridFlag::GenerateTextureCoords) {
++attributeCount;
stride += sizeof(Vector2);
}
Containers::Array<char> vertexData{stride*vertexCount.product()};
Containers::Array<Trade::MeshAttributeData> attributes{attributeCount};
std::size_t attributeIndex = 0;
std::size_t attributeOffset = 0;
/* Fill positions */
Containers::StridedArrayView1D<Vector3> positions{vertexData,
reinterpret_cast<Vector3*>(vertexData.begin()),
std::size_t(vertexCount.product()), std::ptrdiff_t(stride)};
attributes[attributeIndex++] =
Trade::MeshAttributeData{Trade::MeshAttribute::Position, positions};
attributeOffset += sizeof(Vector3);
{
std::size_t i = 0;
for(Int y = 0; y != vertexCount.y(); ++y)
for(Int x = 0; x != vertexCount.x(); ++x)
positions[i++] = {(Vector2(x, y)/Vector2(faceCount))*2.0f - Vector2{1.0f}, 0.0f};
}
/* Fill normals, if any. It's always the second attribute, right after
positions. */
if(flags & GridFlag::GenerateNormals) {
Containers::StridedArrayView1D<Vector3> normals{vertexData,
reinterpret_cast<Vector3*>(vertexData.begin() + attributeOffset),
std::size_t(vertexCount.product()), std::ptrdiff_t(stride)};
attributes[attributeIndex++] =
Trade::MeshAttributeData{Trade::MeshAttribute::Normal, normals};
attributeOffset += sizeof(Vector3);
for(auto&& i: normals) i = Vector3::zAxis(1.0f);
}
std::vector<std::vector<Vector2>> textureCoordinates;
if(flags & GridFlag::GenerateTextureCoords) {
textureCoordinates.emplace_back();
textureCoordinates[0].reserve(positions.size());
Containers::StridedArrayView1D<Vector2> textureCoords{vertexData,
reinterpret_cast<Vector2*>(vertexData.begin() + attributeOffset),
std::size_t(vertexCount.product()), std::ptrdiff_t(stride)};
attributes[attributeIndex++] =
Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates, textureCoords};
attributeOffset += sizeof(Vector2);
for(std::size_t i = 0; i != positions.size(); ++i)
textureCoordinates[0].emplace_back(positions[i].xy()*0.5f + Vector2{0.5f});
textureCoords[i] = positions[i].xy()*0.5f + Vector2{0.5f};
}
return Trade::MeshData3D{MeshPrimitive::Triangles, std::move(indices), {std::move(positions)}, std::move(normals), std::move(textureCoordinates), {}, nullptr};
return Trade::MeshData{MeshPrimitive::Triangles,
std::move(indexData), Trade::MeshIndexData{indices},
std::move(vertexData), std::move(attributes)};
}
Trade::MeshData3D grid3DWireframe(const Vector2i& subdivisions) {
Trade::MeshData grid3DWireframe(const Vector2i& subdivisions) {
const Vector2i vertexCount = subdivisions + Vector2i{2};
const Vector2i faceCount = subdivisions + Vector2i{1};
std::vector<Vector3> positions;
positions.reserve(vertexCount.product());
for(Int y = 0; y != vertexCount.y(); ++y)
for(Int x = 0; x != vertexCount.x(); ++x)
positions.emplace_back((Vector2(x, y)/Vector2(faceCount))*2.0f - Vector2{1.0f}, 0.0f);
std::vector<UnsignedInt> indices;
indices.reserve(vertexCount.y()*(vertexCount.x() - 1)*2 +
vertexCount.x()*(vertexCount.y() - 1)*2);
for(Int y = 0; y != vertexCount.y(); ++y) {
for(Int x = 0; x != vertexCount.x(); ++x) {
/* 3 7
| | ...
2 6
0--1 4--5 ... */
if(x != vertexCount.x() - 1) indices.insert(indices.end(), {
UnsignedInt(y*vertexCount.x() + x),
UnsignedInt(y*vertexCount.x() + x + 1)});
if(y != vertexCount.y() - 1) indices.insert(indices.end(), {
UnsignedInt(y*vertexCount.x() + x),
UnsignedInt((y + 1)*vertexCount.x() + x)});
Containers::Array<char> indexData{sizeof(UnsignedInt)*
(vertexCount.y()*(vertexCount.x() - 1)*2 +
vertexCount.x()*(vertexCount.y() - 1)*2)};
auto indices = Containers::arrayCast<UnsignedInt>(indexData);
{
std::size_t i = 0;
for(Int y = 0; y != vertexCount.y(); ++y) {
for(Int x = 0; x != vertexCount.x(); ++x) {
/* 3 7
| | ...
2 6
0--1 4--5 ... */
if(x != vertexCount.x() - 1) {
indices[i++] = UnsignedInt(y*vertexCount.x() + x);
indices[i++] = UnsignedInt(y*vertexCount.x() + x + 1);
}
if(y != vertexCount.y() - 1) {
indices[i++] = UnsignedInt(y*vertexCount.x() + x);
indices[i++] = UnsignedInt((y + 1)*vertexCount.x() + x);
}
}
}
}
return Trade::MeshData3D{MeshPrimitive::Lines, std::move(indices), {std::move(positions)}, {}, {}, {}, nullptr};
Containers::Array<char> vertexData{sizeof(Vector3)*vertexCount.product()};
auto positions = Containers::arrayCast<Vector3>(vertexData);
{
std::size_t i = 0;
for(Int y = 0; y != vertexCount.y(); ++y)
for(Int x = 0; x != vertexCount.x(); ++x)
positions[i++] = {(Vector2(x, y)/Vector2(faceCount))*2.0f - Vector2{1.0f}, 0.0f};
}
return Trade::MeshData{MeshPrimitive::Lines,
std::move(indexData), Trade::MeshIndexData{indices},
std::move(vertexData), {Trade::MeshAttributeData{Trade::MeshAttribute::Position, positions}}};
}
}}

19
src/Magnum/Primitives/Grid.h

@ -66,8 +66,11 @@ CORRADE_ENUMSET_OPERATORS(GridFlags)
/**
@brief Solid 3D grid
2x2 grid in the XY plane with normals in positive Z direction. Indexed
@ref MeshPrimitive::Triangles with optional normals and texture coordinates.
2x2 grid in the XY plane with normals in positive Z direction.
@ref MeshPrimitive::Triangles with @ref MeshIndexType::UnsignedInt indices,
interleaved @ref VertexFormat::Vector3 positions, optional
@ref VertexFormat::Vector3 normals and @ref VertexFormat::Vector2 texture
coordinates.
@image html primitives-grid3dsolid.png width=256px
@ -78,12 +81,14 @@ cells horizontally and 4 vertically. In particular, this is different from the
`subdivisions` parameter in @ref icosphereSolid().
@see @ref grid3DWireframe()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D grid3DSolid(const Vector2i& subdivisions, GridFlags flags = GridFlag::GenerateNormals);
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData grid3DSolid(const Vector2i& subdivisions, GridFlags flags = GridFlag::GenerateNormals);
/**
@brief Wireframe 3D grid
2x2 grid in the XY plane. Indexed @ref MeshPrimitive::Lines.
2x2 grid in the XY plane. @ref MeshPrimitive::Lines with
@ref MeshIndexType::UnsignedInt indices and @ref VertexFormat::Vector3
positions.
@image html primitives-grid3dwireframe.png width=256px
@ -91,10 +96,12 @@ The @p subdivisions parameter describes how many times the plane gets cut in
each direction. Specifying @cpp {0, 0} @ce will make the result an (indexed)
equivalent to @ref planeWireframe(); @cpp {5, 3} @ce will make the grid have 6
cells horizontally and 4 vertically. In particular, this is different from the
`subdivisions` parameter in @ref icosphereSolid().
`subdivisions` parameter in @ref icosphereSolid(). Also please note the grid
has vertices in each intersection to be suitable for deformation along the Z
axis --- not just long lines crossing each other.
@see @ref grid3DSolid()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D grid3DWireframe(const Vector2i& subdivisions);
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData grid3DWireframe(const Vector2i& subdivisions);
}}

131
src/Magnum/Primitives/Icosphere.cpp

@ -25,62 +25,103 @@
#include "Icosphere.h"
#include <Corrade/Containers/GrowableArray.h>
#include "Magnum/Mesh.h"
#include "Magnum/Math/Color.h"
#include "Magnum/Math/Vector3.h"
#include "Magnum/MeshTools/RemoveDuplicates.h"
#include "Magnum/MeshTools/Subdivide.h"
#include "Magnum/Trade/MeshData3D.h"
#include "Magnum/Trade/MeshData.h"
namespace Magnum { namespace Primitives {
Trade::MeshData3D icosphereSolid(const UnsignedInt subdivisions) {
std::vector<UnsignedInt> indices{
1, 2, 6,
1, 7, 2,
3, 4, 5,
4, 3, 8,
6, 5, 11,
5, 6, 10,
9, 10, 2,
10, 9, 3,
7, 8, 9,
8, 7, 0,
11, 0, 1,
0, 11, 4,
6, 2, 10,
1, 6, 11,
3, 5, 10,
5, 4, 11,
2, 7, 9,
7, 1, 0,
3, 9, 8,
4, 8, 0
};
namespace {
constexpr UnsignedInt Indices[]{
1, 2, 6,
1, 7, 2,
3, 4, 5,
4, 3, 8,
6, 5, 11,
5, 6, 10,
9, 10, 2,
10, 9, 3,
7, 8, 9,
8, 7, 0,
11, 0, 1,
0, 11, 4,
6, 2, 10,
1, 6, 11,
3, 5, 10,
5, 4, 11,
2, 7, 9,
7, 1, 0,
3, 9, 8,
4, 8, 0
};
constexpr Vector3 Positions[]{
{0.0f, -0.525731f, 0.850651f},
{0.850651f, 0.0f, 0.525731f},
{0.850651f, 0.0f, -0.525731f},
{-0.850651f, 0.0f, -0.525731f},
{-0.850651f, 0.0f, 0.525731f},
{-0.525731f, 0.850651f, 0.0f},
{0.525731f, 0.850651f, 0.0f},
{0.525731f, -0.850651f, 0.0f},
{-0.525731f, -0.850651f, 0.0f},
{0.0f, -0.525731f, -0.850651f},
{0.0f, 0.525731f, -0.850651f},
{0.0f, 0.525731f, 0.850651f}
};
}
Trade::MeshData icosphereSolid(const UnsignedInt subdivisions) {
const std::size_t indexCount = Containers::arraySize(Indices)*(1 << subdivisions*2);
const std::size_t vertexCount = Containers::arraySize(Positions) + ((indexCount - Containers::arraySize(Indices))/3);
Containers::Array<char> indexData{indexCount*sizeof(UnsignedInt)};
auto indices = Containers::arrayCast<UnsignedInt>(indexData);
std::memcpy(indices.begin(), Indices, sizeof(Indices));
std::vector<Vector3> positions{
{0.0f, -0.525731f, 0.850651f},
{0.850651f, 0.0f, 0.525731f},
{0.850651f, 0.0f, -0.525731f},
{-0.850651f, 0.0f, -0.525731f},
{-0.850651f, 0.0f, 0.525731f},
{-0.525731f, 0.850651f, 0.0f},
{0.525731f, 0.850651f, 0.0f},
{0.525731f, -0.850651f, 0.0f},
{-0.525731f, -0.850651f, 0.0f},
{0.0f, -0.525731f, -0.850651f},
{0.0f, 0.525731f, -0.850651f},
{0.0f, 0.525731f, 0.850651f}
struct Vertex {
Vector3 position;
Vector3 normal;
};
Containers::Array<char> vertexData;
arrayResize(vertexData, Containers::NoInit, sizeof(Vertex)*vertexCount);
/* Build up the subdivided positions */
{
auto vertices = Containers::arrayCast<Vertex>(vertexData);
Containers::StridedArrayView1D<Vector3> positions{vertices, &vertices[0].position, vertices.size(), sizeof(Vertex)};
for(std::size_t i = 0; i != Containers::arraySize(Positions); ++i)
positions[i] = Positions[i];
for(std::size_t i = 0; i != subdivisions; ++i) {
const std::size_t iterationIndexCount = Containers::arraySize(Indices)*(1 << (i + 1)*2);
const std::size_t iterationVertexCount = Containers::arraySize(Positions) + ((iterationIndexCount - Containers::arraySize(Indices))/3);
MeshTools::subdivideInPlace(indices.prefix(iterationIndexCount), positions.prefix(iterationVertexCount), [](const Vector3& a, const Vector3& b) {
return (a+b).normalized();
});
}
for(std::size_t i = 0; i != subdivisions; ++i)
MeshTools::subdivide(indices, positions, [](const Vector3& a, const Vector3& b) {
return (a+b).normalized();
});
/** @todo i need arrayShrinkAndGiveUpMemoryIfItDoesntCauseRealloc() */
arrayResize(vertexData, MeshTools::removeDuplicatesIndexedInPlace(Containers::stridedArrayView(indices), Containers::stridedArrayView(positions))*sizeof(Vertex));
}
positions.resize(MeshTools::removeDuplicatesIndexedInPlace(Containers::stridedArrayView(indices), Containers::stridedArrayView(positions)));
/* Build up the views again with correct size, fill the normals */
auto vertices = Containers::arrayCast<Vertex>(vertexData);
Containers::StridedArrayView1D<Vector3> positions{vertices, &vertices[0].position, vertices.size(), sizeof(Vertex)};
Containers::StridedArrayView1D<Vector3> normals{vertices, &vertices[0].normal, vertices.size(), sizeof(Vertex)};
for(std::size_t i = 0; i != positions.size(); ++i)
normals[i] = positions[i];
std::vector<Vector3> normals(positions);
return Trade::MeshData3D{MeshPrimitive::Triangles, std::move(indices), {std::move(positions)}, {std::move(normals)}, {}, {}, nullptr};
return Trade::MeshData{MeshPrimitive::Triangles, std::move(indexData),
Trade::MeshIndexData{indices}, std::move(vertexData),
{Trade::MeshAttributeData{Trade::MeshAttribute::Position, positions},
Trade::MeshAttributeData{Trade::MeshAttribute::Normal, normals}}};
}
}}

7
src/Magnum/Primitives/Icosphere.h

@ -38,8 +38,9 @@ namespace Magnum { namespace Primitives {
@brief Solid 3D icosphere
@param subdivisions Number of subdivisions
Sphere with radius @cpp 1.0f @ce. Indexed @ref MeshPrimitive::Triangles with
normals.
Sphere with radius @cpp 1.0f @ce. @ref MeshPrimitive::Triangles with
@ref MeshIndexType::UnsignedInt indices, interleaved @ref VertexFormat::Vector3
positions and @ref VertexFormat::Vector3 normals.
@image html primitives-icospheresolid.png width=256px
@ -51,7 +52,7 @@ result in 320 faces and so on. In particular, this is different from the
`subdivisions` parameter in @ref grid3DSolid() or @ref grid3DWireframe().
@see @ref uvSphereSolid(), @ref uvSphereWireframe()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D icosphereSolid(UnsignedInt subdivisions);
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData icosphereSolid(UnsignedInt subdivisions);
}}

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

@ -25,25 +25,59 @@
#include "Spheroid.h"
#include <Corrade/Containers/GrowableArray.h>
#include "Magnum/Math/Functions.h"
#include "Magnum/Math/Color.h"
#include "Magnum/Mesh.h"
#include "Magnum/Trade/MeshData3D.h"
#include "Magnum/Trade/MeshData.h"
namespace Magnum { namespace Primitives { namespace Implementation {
Spheroid::Spheroid(UnsignedInt segments, TextureCoords textureCoords): segments(segments), textureCoords(textureCoords) {}
Spheroid::Spheroid(UnsignedInt segments, TextureCoords textureCoords): _segments(segments), _textureCoords(textureCoords) {}
void Spheroid::capVertex(Float y, Float normalY, Float textureCoordsV) {
positions.emplace_back(0.0f, y, 0.0f);
normals.emplace_back(0.0f, normalY, 0.0f);
namespace {
struct Vertex {
Vector3 position;
Vector3 normal;
};
struct VertexTextureCoords {
Vector3 position;
Vector3 normal;
Vector2 textureCoords;
};
}
if(textureCoords == TextureCoords::Generate)
textureCoords2D.emplace_back(0.5, textureCoordsV);
/** @todo gah this is fugly, any idea how to do this less awful? expose
arrayGrow? also, with current growth strategy this might realloc too much
at the beginning since the growth is optimized for adding a single
element */
void Spheroid::append(const Vector3& position, const Vector3& normal, const Vector2& textureCoords) {
if(_textureCoords == TextureCoords::Generate) {
const VertexTextureCoords v[]{{position, normal, textureCoords}};
arrayAppend(_vertexData, Containers::arrayCast<const char>(Containers::arrayView(v)));
} else {
const Vertex v[]{{position, normal}};
arrayAppend(_vertexData, Containers::arrayCast<const char>(Containers::arrayView(v)));
}
}
void Spheroid::setLastVertexTextureCoords(const Vector2& textureCoords) {
/* Assuming append() was called before */
Containers::arrayCast<VertexTextureCoords>(_vertexData).back().textureCoords = textureCoords;
}
void Spheroid::capVertex(Float y, Float normalY, Float textureCoordsV) {
append({0.0f, y, 0.0f}, {0.0f, normalY, 0.0f});
if(_textureCoords == TextureCoords::Generate)
setLastVertexTextureCoords({0.5f, textureCoordsV});
}
void Spheroid::hemisphereVertexRings(UnsignedInt count, Float centerY, Rad startRingAngle, Rad ringAngleIncrement, Float startTextureCoordsV, Float textureCoordsVIncrement) {
const Rad segmentAngleIncrement(Constants::tau()/segments);
const Rad segmentAngleIncrement(Constants::tau()/_segments);
for(UnsignedInt i = 0; i != count; ++i) {
const Rad ringAngle = startRingAngle + Float(i)*ringAngleIncrement;
const std::pair<Float, Float> ringSinCos = Math::sincos(ringAngle);
@ -51,21 +85,23 @@ void Spheroid::hemisphereVertexRings(UnsignedInt count, Float centerY, Rad start
const Float z = ringSinCos.second;
const Float y = ringSinCos.first;
for(UnsignedInt j = 0; j != segments; ++j) {
for(UnsignedInt j = 0; j != _segments; ++j) {
const Rad segmentAngle = Float(j)*segmentAngleIncrement;
const std::pair<Float, Float> segmentSinCos = Math::sincos(segmentAngle);
positions.emplace_back(x*segmentSinCos.first, centerY+y, z*segmentSinCos.second);
normals.emplace_back(x*segmentSinCos.first, y, z*segmentSinCos.second);
append({x*segmentSinCos.first, centerY+y, z*segmentSinCos.second},
{x*segmentSinCos.first, y, z*segmentSinCos.second});
if(textureCoords == TextureCoords::Generate)
textureCoords2D.emplace_back(j*1.0f/segments, startTextureCoordsV + i*textureCoordsVIncrement);
if(_textureCoords == TextureCoords::Generate)
setLastVertexTextureCoords({j*1.0f/_segments, startTextureCoordsV + i*textureCoordsVIncrement});
}
/* Duplicate first segment in the ring for additional vertex for texture coordinate */
if(textureCoords == TextureCoords::Generate) {
positions.push_back(positions[positions.size()-segments]);
normals.push_back(normals[normals.size()-segments]);
textureCoords2D.emplace_back(1.0f, startTextureCoordsV + i*textureCoordsVIncrement);
if(_textureCoords == TextureCoords::Generate) {
/* This view will become dangling right after append() */
auto typedVertices = Containers::arrayCast<VertexTextureCoords>(_vertexData);
append(typedVertices[typedVertices.size()-_segments].position,
typedVertices[typedVertices.size()-_segments].normal,
{1.0f, startTextureCoordsV + i*textureCoordsVIncrement});
}
}
}
@ -74,23 +110,25 @@ void Spheroid::cylinderVertexRings(const UnsignedInt count, const Float startY,
const Vector2 baseNormal = -increment.perpendicular().normalized();
Vector2 base = {1.0f, startY};
const Rad segmentAngleIncrement(Constants::tau()/segments);
const Rad segmentAngleIncrement(Constants::tau()/_segments);
for(UnsignedInt i = 0; i != count; ++i) {
for(UnsignedInt j = 0; j != segments; ++j) {
for(UnsignedInt j = 0; j != _segments; ++j) {
const Rad segmentAngle = Float(j)*segmentAngleIncrement;
const std::pair<Float, Float> segmentSinCos = Math::sincos(segmentAngle);
positions.emplace_back(base.x()*segmentSinCos.first, base.y(), base.x()*segmentSinCos.second);
normals.emplace_back(baseNormal.x()*segmentSinCos.first, baseNormal.y(), baseNormal.x()*segmentSinCos.second);
append({base.x()*segmentSinCos.first, base.y(), base.x()*segmentSinCos.second},
{baseNormal.x()*segmentSinCos.first, baseNormal.y(), baseNormal.x()*segmentSinCos.second});
if(textureCoords == TextureCoords::Generate)
textureCoords2D.emplace_back(j*1.0f/segments, startTextureCoordsV + i*textureCoordsVIncrement);
if(_textureCoords == TextureCoords::Generate)
setLastVertexTextureCoords({j*1.0f/_segments, startTextureCoordsV + i*textureCoordsVIncrement});
}
/* Duplicate first segment in the ring for additional vertex for texture coordinate */
if(textureCoords == TextureCoords::Generate) {
positions.push_back(positions[positions.size()-segments]);
normals.push_back(normals[normals.size()-segments]);
textureCoords2D.emplace_back(1.0f, startTextureCoordsV + i*textureCoordsVIncrement);
if(_textureCoords == TextureCoords::Generate) {
/* This view will become dangling right after append() */
auto typedVertices = Containers::arrayCast<VertexTextureCoords>(_vertexData);
append(typedVertices[typedVertices.size()-_segments].position,
typedVertices[typedVertices.size()-_segments].normal,
{1.0f, startTextureCoordsV + i*textureCoordsVIncrement});
}
base += increment;
@ -98,80 +136,118 @@ void Spheroid::cylinderVertexRings(const UnsignedInt count, const Float startY,
}
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);
for(UnsignedInt j = 0; j != _segments; ++j) {
arrayAppend(_indexData, {
/* Bottom vertex */
0u,
/* Top right vertex */
(j != _segments-1 || _textureCoords == TextureCoords::Generate) ?
j+2 : 1,
/* Top left vertex */
j+1
});
}
}
void Spheroid::faceRings(UnsignedInt count, UnsignedInt offset) {
const UnsignedInt vertexSegments = segments + (textureCoords == TextureCoords::Generate ? 1 : 0);
const UnsignedInt vertexSegments = _segments + (_textureCoords == TextureCoords::Generate ? 1 : 0);
for(UnsignedInt i = 0; i != count; ++i) {
for(UnsignedInt j = 0; j != segments; ++j) {
for(UnsignedInt j = 0; j != _segments; ++j) {
const UnsignedInt bottomLeft = i*vertexSegments+j+offset;
const UnsignedInt bottomRight = ((j != segments-1 || textureCoords == TextureCoords::Generate) ?
i*vertexSegments+j+1+offset : i*segments+offset);
const UnsignedInt bottomRight = ((j != _segments-1 || _textureCoords == TextureCoords::Generate) ?
i*vertexSegments+j+1+offset : i*_segments+offset);
const UnsignedInt topLeft = bottomLeft+vertexSegments;
const 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);
arrayAppend(_indexData, {
bottomLeft,
bottomRight,
topRight,
bottomLeft,
topRight,
topLeft
});
}
}
}
void Spheroid::topFaceRing() {
const UnsignedInt vertexSegments = segments + (textureCoords == TextureCoords::Generate ? 1 : 0);
for(UnsignedInt j = 0; j != segments; ++j) {
/* Bottom left vertex */
indices.push_back(normals.size()-vertexSegments+j-1);
/* Bottom right vertex */
indices.push_back((j != segments-1 || textureCoords == TextureCoords::Generate) ?
normals.size()-vertexSegments+j : normals.size()-segments-1);
/* Top vertex */
indices.push_back(normals.size()-1);
const UnsignedInt vertexSegments = _segments + (_textureCoords == TextureCoords::Generate ? 1 : 0);
UnsignedInt vertexCount;
if(_textureCoords == TextureCoords::Generate)
vertexCount = _vertexData.size()/sizeof(VertexTextureCoords);
else
vertexCount = _vertexData.size()/sizeof(Vertex);
for(UnsignedInt j = 0; j != _segments; ++j) {
arrayAppend(_indexData, {
/* Bottom left vertex */
vertexCount - vertexSegments + j - 1,
/* Bottom right vertex */
(j != _segments-1 || _textureCoords == TextureCoords::Generate) ?
vertexCount - vertexSegments + j : vertexCount - _segments - 1,
/* Top vertex */
vertexCount - 1
});
}
}
void Spheroid::capVertexRing(Float y, Float textureCoordsV, const Vector3& normal) {
const Rad segmentAngleIncrement(Constants::tau()/segments);
const Rad segmentAngleIncrement(Constants::tau()/_segments);
for(UnsignedInt i = 0; i != segments; ++i) {
for(UnsignedInt i = 0; i != _segments; ++i) {
const Rad segmentAngle = Float(i)*segmentAngleIncrement;
const std::pair<Float, Float> segmentSinCos = Math::sincos(segmentAngle);
positions.emplace_back(segmentSinCos.first, y, segmentSinCos.second);
normals.push_back(normal);
append({segmentSinCos.first, y, segmentSinCos.second}, normal);
if(textureCoords == TextureCoords::Generate)
textureCoords2D.emplace_back(i*1.0f/segments, textureCoordsV);
if(_textureCoords == TextureCoords::Generate)
setLastVertexTextureCoords({i*1.0f/_segments, textureCoordsV});
}
/* Duplicate first segment in the ring for additional vertex for texture coordinate */
if(textureCoords == TextureCoords::Generate) {
positions.push_back(positions[positions.size()-segments]);
normals.push_back(normal);
textureCoords2D.emplace_back(1.0f, textureCoordsV);
if(_textureCoords == TextureCoords::Generate) {
/* This view will become dangling right after append() */
auto typedVertices = Containers::arrayCast<VertexTextureCoords>(_vertexData);
append(typedVertices[typedVertices.size()-_segments].position,
normal,
{1.0f, textureCoordsV});
}
}
Trade::MeshData3D Spheroid::finalize() {
return Trade::MeshData3D{MeshPrimitive::Triangles, std::move(indices), {std::move(positions)}, {std::move(normals)},
textureCoords == TextureCoords::Generate ? std::vector<std::vector<Vector2>>{std::move(textureCoords2D)} : std::vector<std::vector<Vector2>>(), {}, nullptr};
Trade::MeshData Spheroid::finalize() {
Trade::MeshIndexData indices{_indexData};
const std::size_t stride = _textureCoords == TextureCoords::Generate ?
sizeof(VertexTextureCoords) : sizeof(Vertex);
const std::size_t size = _vertexData.size()/stride;
auto typedVertices = reinterpret_cast<VertexTextureCoords*>(_vertexData.data());
Trade::MeshAttributeData positions{Trade::MeshAttribute::Position,
Containers::stridedArrayView(_vertexData, &typedVertices[0].position,
size, stride)};
Trade::MeshAttributeData normals{Trade::MeshAttribute::Normal,
Containers::stridedArrayView(_vertexData, &typedVertices[0].normal,
size, stride)};
Containers::Array<Trade::MeshAttributeData> attributes;
if(_textureCoords == TextureCoords::Generate) {
Trade::MeshAttributeData textureCoords{Trade::MeshAttribute::TextureCoordinates,
Containers::stridedArrayView(_vertexData, &typedVertices[0].textureCoords,
size, stride)};
attributes = Containers::Array<Trade::MeshAttributeData>{Containers::InPlaceInit, {positions, normals, textureCoords}};
} else {
attributes = Containers::Array<Trade::MeshAttributeData>{Containers::InPlaceInit, {positions, normals}};
}
return Trade::MeshData{MeshPrimitive::Triangles,
Containers::arrayAllocatorCast<char>(std::move(_indexData)), indices,
std::move(_vertexData), std::move(attributes)};
}
}}}

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

@ -25,9 +25,11 @@
DEALINGS IN THE SOFTWARE.
*/
#include <vector>
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/StridedArrayView.h>
#include "Magnum/Magnum.h"
#include "Magnum/Math/Vector2.h"
#include "Magnum/Trade/Trade.h"
namespace Magnum { namespace Primitives { namespace Implementation {
@ -49,16 +51,17 @@ class Spheroid {
void topFaceRing();
void capVertexRing(Float y, Float textureCoordsV, const Vector3& normal);
Trade::MeshData3D finalize();
Trade::MeshData finalize();
private:
UnsignedInt segments;
TextureCoords textureCoords;
UnsignedInt _segments;
TextureCoords _textureCoords;
std::vector<UnsignedInt> indices;
std::vector<Vector3> positions;
std::vector<Vector3> normals;
std::vector<Vector2> textureCoords2D;
Containers::Array<UnsignedInt> _indexData;
Containers::Array<char> _vertexData;
void append(const Vector3& position, const Vector3& normal, const Vector2& textureCoords = {});
void setLastVertexTextureCoords(const Vector2& textureCoords);
};
}}}

64
src/Magnum/Primitives/Implementation/WireframeSpheroid.cpp

@ -25,24 +25,26 @@
#include "WireframeSpheroid.h"
#include <Corrade/Containers/GrowableArray.h>
#include "Magnum/Math/Functions.h"
#include "Magnum/Math/Color.h"
#include "Magnum/Mesh.h"
#include "Magnum/Trade/MeshData3D.h"
#include "Magnum/Trade/MeshData.h"
namespace Magnum { namespace Primitives { namespace Implementation {
WireframeSpheroid::WireframeSpheroid(const UnsignedInt segments): _segments(segments) {}
void WireframeSpheroid::bottomHemisphere(const Float endY, const UnsignedInt rings) {
CORRADE_INTERNAL_ASSERT(_positions.empty());
CORRADE_INTERNAL_ASSERT(_vertexData.empty());
/* Initial vertex */
_positions.push_back(Vector3::yAxis(endY - 1.0f));
arrayAppend(_vertexData, Vector3::yAxis(endY - 1.0f));
/* Connect initial vertex to first ring */
for(UnsignedInt i = 0; i != 4; ++i)
_indices.insert(_indices.end(), {0, i+1});
arrayAppend(_indexData, {0u, i+1});
/* Hemisphere vertices and indices */
const Rad ringAngleIncrement(Constants::piHalf()/rings);
@ -50,21 +52,24 @@ void WireframeSpheroid::bottomHemisphere(const Float endY, const UnsignedInt rin
const Rad angle = Float(j+1)*ringAngleIncrement;
const std::pair<Float, Float> sincos = Math::sincos(angle);
_positions.emplace_back(0.0f, endY - sincos.second, sincos.first);
_positions.emplace_back(sincos.first, endY - sincos.second, 0.0f);
_positions.emplace_back(0.0f, endY - sincos.second, -sincos.first);
_positions.emplace_back(-sincos.first, endY - sincos.second, 0.0f);
arrayAppend(_vertexData, {
{0.0f, endY - sincos.second, sincos.first},
{sincos.first, endY - sincos.second, 0.0f},
{0.0f, endY - sincos.second, -sincos.first},
{-sincos.first, endY - sincos.second, 0.0f}
});
/* Connect vertices to next ring */
for(UnsignedInt i = 0; i != 4; ++i)
_indices.insert(_indices.end(), {UnsignedInt(_positions.size())-4+i, UnsignedInt(_positions.size())+i});
for(UnsignedInt i = 0; i != 4; ++i) {
arrayAppend(_indexData, {UnsignedInt(_vertexData.size()) - 4 + i, UnsignedInt(_vertexData.size()) + i});
}
}
}
void WireframeSpheroid::topHemisphere(const Float startY, const UnsignedInt rings) {
/* Connect previous ring to following vertices (if any) */
if(rings > 1) for(UnsignedInt i = 0; i != 4; ++i) {
_indices.insert(_indices.end(), {UnsignedInt(_positions.size())-4*_segments+i, UnsignedInt(_positions.size())+i});
arrayAppend(_indexData, {UnsignedInt(_vertexData.size()) - 4*_segments + i, UnsignedInt(_vertexData.size()) + i});
}
/* Hemisphere vertices and indices */
@ -74,23 +79,26 @@ void WireframeSpheroid::topHemisphere(const Float startY, const UnsignedInt ring
const std::pair<Float, Float> sincos = Math::sincos(angle);
/* Connect previous hemisphere ring to current vertices */
if(j != 0) for(UnsignedInt i = 0; i != 4; ++i)
_indices.insert(_indices.end(), {UnsignedInt(_positions.size())-4+i, UnsignedInt(_positions.size())+i});
if(j != 0) for(UnsignedInt i = 0; i != 4; ++i) {
arrayAppend(_indexData, {UnsignedInt(_vertexData.size()) - 4 + i, UnsignedInt(_vertexData.size()) + i});
}
_positions.emplace_back(0.0f, startY + sincos.first, sincos.second);
_positions.emplace_back(sincos.second, startY + sincos.first, 0.0f);
_positions.emplace_back(0.0f, startY + sincos.first, -sincos.second);
_positions.emplace_back(-sincos.second, startY + sincos.first, 0.0f);
arrayAppend(_vertexData, {
{0.0f, startY + sincos.first, sincos.second},
{sincos.second, startY + sincos.first, 0.0f},
{0.0f, startY + sincos.first, -sincos.second},
{-sincos.second, startY + sincos.first, 0.0f}
});
}
/* Final vertex */
_positions.push_back(Vector3::yAxis(startY + 1.0f));
arrayAppend(_vertexData, Vector3::yAxis(startY + 1.0f));
/* Connect last ring to final vertex */
if(rings > 1) for(UnsignedInt i = 0; i != 4; ++i)
_indices.insert(_indices.end(), {UnsignedInt(_positions.size()) -5 + i, UnsignedInt(_positions.size()) - 1});
arrayAppend(_indexData, {UnsignedInt(_vertexData.size()) - 5 + i, UnsignedInt(_vertexData.size()) - 1});
else for(UnsignedInt i = 0; i != 4; ++i)
_indices.insert(_indices.end(), {UnsignedInt(_positions.size()) - 4*_segments + i- 1 , UnsignedInt(_positions.size()) - 1});
arrayAppend(_indexData, {UnsignedInt(_vertexData.size()) - 4*_segments + i - 1, UnsignedInt(_vertexData.size()) - 1});
}
void WireframeSpheroid::ring(const Float y) {
@ -100,24 +108,28 @@ void WireframeSpheroid::ring(const Float y) {
for(UnsignedInt i = 0; i != 4; ++i) {
const Rad segmentAngle = Rad(Float(i)*Constants::piHalf()) + Float(j)*segmentAngleIncrement;
const std::pair<Float, Float> sincos = Math::sincos(segmentAngle);
if(j != 0) _indices.insert(_indices.end(), {UnsignedInt(_positions.size()-4), UnsignedInt(_positions.size())});
_positions.emplace_back(sincos.first, y, sincos.second);
if(j != 0) arrayAppend(_indexData, {UnsignedInt(_vertexData.size() - 4), UnsignedInt(_vertexData.size())});
arrayAppend(_vertexData, {sincos.first, y, sincos.second});
}
}
/* Close the ring */
for(UnsignedInt i = 0; i != 4; ++i)
_indices.insert(_indices.end(), {UnsignedInt(_positions.size())-4+i, UnsignedInt(_positions.size())-4*_segments+(i+1)%4});
arrayAppend(_indexData, {UnsignedInt(_vertexData.size()) - 4 + i, UnsignedInt(_vertexData.size()) - 4*_segments + (i + 1)%4});
}
void WireframeSpheroid::cylinder() {
/* Connect four vertex pairs of previous and next ring */
for(UnsignedInt i = 0; i != 4; ++i)
_indices.insert(_indices.end(), {UnsignedInt(_positions.size())-4*_segments+i, UnsignedInt(_positions.size())+i});
arrayAppend(_indexData, {UnsignedInt(_vertexData.size()) - 4*_segments + i, UnsignedInt(_vertexData.size()) + i});
}
Trade::MeshData3D WireframeSpheroid::finalize() {
return Trade::MeshData3D{MeshPrimitive::Lines, std::move(_indices), {std::move(_positions)}, {}, {}, {}, nullptr};
Trade::MeshData WireframeSpheroid::finalize() {
Trade::MeshIndexData indices{_indexData};
Trade::MeshAttributeData positions{Trade::MeshAttribute::Position, Containers::arrayView(_vertexData)};
return Trade::MeshData{MeshPrimitive::Lines,
Containers::arrayAllocatorCast<char>(std::move(_indexData)), indices,
Containers::arrayAllocatorCast<char>(std::move(_vertexData)), {positions}};
}
}}}

8
src/Magnum/Primitives/Implementation/WireframeSpheroid.h

@ -25,7 +25,7 @@
DEALINGS IN THE SOFTWARE.
*/
#include <vector>
#include <Corrade/Containers/Array.h>
#include "Magnum/Magnum.h"
#include "Magnum/Trade/Trade.h"
@ -41,13 +41,13 @@ class WireframeSpheroid {
void ring(Float y);
void cylinder();
Trade::MeshData3D finalize();
Trade::MeshData finalize();
private:
UnsignedInt _segments;
std::vector<UnsignedInt> _indices;
std::vector<Vector3> _positions;
Containers::Array<UnsignedInt> _indexData;
Containers::Array<Vector3> _vertexData;
};
}}}

27
src/Magnum/Primitives/Line.cpp

@ -27,24 +27,35 @@
#include "Magnum/Mesh.h"
#include "Magnum/Math/Color.h"
#include "Magnum/Trade/MeshData2D.h"
#include "Magnum/Trade/MeshData3D.h"
#include "Magnum/Trade/MeshData.h"
namespace Magnum { namespace Primitives {
Trade::MeshData2D line2D(const Vector2& a, const Vector2& b) {
return Trade::MeshData2D{MeshPrimitive::Lines, {}, {{a, b}}, {}, {}, nullptr};
Trade::MeshData line2D(const Vector2& a, const Vector2& b) {
Containers::Array<char> vertexData{sizeof(Vector2)*2};
auto positions = Containers::arrayCast<Vector2>(vertexData);
positions[0] = a;
positions[1] = b;
return Trade::MeshData{MeshPrimitive::Lines, std::move(vertexData),
{Trade::MeshAttributeData{Trade::MeshAttribute::Position, positions}}};
}
Trade::MeshData3D line3D(const Vector3& a, const Vector3& b) {
return Trade::MeshData3D{MeshPrimitive::Lines, {}, {{a, b}}, {}, {}, {}, nullptr};
Trade::MeshData line3D(const Vector3& a, const Vector3& b) {
Containers::Array<char> vertexData{sizeof(Vector3)*2};
auto positions = Containers::arrayCast<Vector3>(vertexData);
positions[0] = a;
positions[1] = b;
return Trade::MeshData{MeshPrimitive::Lines, std::move(vertexData),
{Trade::MeshAttributeData{Trade::MeshAttribute::Position, positions}}};
}
Trade::MeshData2D line2D() {
Trade::MeshData line2D() {
return line2D({0.0f, 0.0f}, {1.0f, 0.0f});
}
Trade::MeshData3D line3D() {
Trade::MeshData line3D() {
return line3D({0.0f, 0.0f, 0.0f}, {1.0f, 0.0f, 0.0f});
}

14
src/Magnum/Primitives/Line.h

@ -38,14 +38,15 @@ namespace Magnum { namespace Primitives {
/**
@brief 2D line
Non-indexed @ref MeshPrimitive::Lines going from @p a to @p b.
Non-indexed @ref MeshPrimitive::Lines with @ref VertexFormat::Vector2 positions
going from @p a to @p b.
@image html primitives-line2d.png width=256px
@see @ref line3D(), @ref line3D(const Vector3&, const Vector3&), @ref axis2D(),
@ref crosshair2D()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData2D line2D(const Vector2& a, const Vector2& b);
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData line2D(const Vector2& a, const Vector2& b);
/**
@brief 2D line in an identity transformation
@ -54,19 +55,20 @@ Equivalent to calling @ref line2D(const Vector2&, const Vector2&) as
@snippet MagnumPrimitives.cpp line2D-identity
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData2D line2D();
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData line2D();
/**
@brief 3D line
Non-indexed @ref MeshPrimitive::Lines going from @p a to @p b.
Non-indexed @ref MeshPrimitive::Lines with @ref VertexFormat::Vector3 positions
going from @p a to @p b.
@image html primitives-line3d.png width=256px
@see @ref line3D(), @ref line2D(const Vector2&, const Vector2&), @ref axis3D(),
@ref crosshair3D()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D line3D(const Vector3& a, const Vector3& b);
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData line3D(const Vector3& a, const Vector3& b);
/**
@brief 3D line in an identity transformation
@ -76,7 +78,7 @@ Unit-size line in direction of positive X axis. Equivalent to calling
@snippet MagnumPrimitives.cpp line3D-identity
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D line3D();
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData line3D();
}}

105
src/Magnum/Primitives/Plane.cpp

@ -26,40 +26,87 @@
#include "Plane.h"
#include "Magnum/Mesh.h"
#include "Magnum/Math/Color.h"
#include "Magnum/Trade/MeshData3D.h"
#include "Magnum/Math/Vector3.h"
#include "Magnum/Trade/MeshData.h"
namespace Magnum { namespace Primitives {
Trade::MeshData3D planeSolid(const PlaneTextureCoords textureCoords) {
std::vector<std::vector<Vector2>> coords;
if(textureCoords == PlaneTextureCoords::Generate) coords.push_back({
{1.0f, 0.0f},
{1.0f, 1.0f},
{0.0f, 0.0f},
{0.0f, 1.0f}
});
return Trade::MeshData3D{MeshPrimitive::TriangleStrip, {}, {{
{1.0f, -1.0f, 0.0f},
{1.0f, 1.0f, 0.0f},
{-1.0f, -1.0f, 0.0f},
{-1.0f, 1.0f, 0.0f}
}}, {{
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f}
}}, std::move(coords), {}, nullptr};
namespace {
constexpr struct VertexSolid {
Vector3 position;
Vector3 normal;
} VerticesSolid[] {
{{ 1.0f, -1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}},
{{ 1.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}},
{{-1.0f, -1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}},
{{-1.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}}
};
constexpr struct VertexSolidTextureCoords {
Vector3 position;
Vector3 normal;
Vector2 textureCoords;
} VerticesSolidTextureCoords[] {
{VerticesSolid[0].position, VerticesSolid[0].normal, {1.0f, 0.0f}},
{VerticesSolid[1].position, VerticesSolid[1].normal, {1.0f, 1.0f}},
{VerticesSolid[2].position, VerticesSolid[2].normal, {0.0f, 0.0f}},
{VerticesSolid[3].position, VerticesSolid[3].normal, {0.0f, 1.0f}}
};
constexpr Trade::MeshAttributeData AttributesSolid[]{
Trade::MeshAttributeData{Trade::MeshAttribute::Position,
Containers::stridedArrayView(VerticesSolid, &VerticesSolid[0].position,
Containers::arraySize(VerticesSolid), sizeof(VertexSolid))},
Trade::MeshAttributeData{Trade::MeshAttribute::Normal,
Containers::stridedArrayView(VerticesSolid, &VerticesSolid[0].normal,
Containers::arraySize(VerticesSolid), sizeof(VertexSolid))}
};
constexpr Trade::MeshAttributeData AttributesSolidTextureCoords[]{
Trade::MeshAttributeData{Trade::MeshAttribute::Position,
Containers::stridedArrayView(VerticesSolidTextureCoords,
&VerticesSolidTextureCoords[0].position,
Containers::arraySize(VerticesSolidTextureCoords), sizeof(VertexSolidTextureCoords))},
Trade::MeshAttributeData{Trade::MeshAttribute::Normal,
Containers::stridedArrayView(VerticesSolidTextureCoords,
&VerticesSolidTextureCoords[0].normal,
Containers::arraySize(VerticesSolidTextureCoords), sizeof(VertexSolidTextureCoords))},
Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates,
Containers::stridedArrayView(VerticesSolidTextureCoords,
&VerticesSolidTextureCoords[0].textureCoords,
Containers::arraySize(VerticesSolidTextureCoords), sizeof(VertexSolidTextureCoords))},
};
}
Trade::MeshData planeSolid(const PlaneTextureCoords textureCoords) {
if(textureCoords != PlaneTextureCoords::Generate)
return Trade::MeshData{MeshPrimitive::TriangleStrip,
{}, VerticesSolid,
Trade::meshAttributeDataNonOwningArray(AttributesSolid)};
return Trade::MeshData{MeshPrimitive::TriangleStrip,
{}, VerticesSolidTextureCoords,
Trade::meshAttributeDataNonOwningArray(AttributesSolidTextureCoords)};
}
namespace {
constexpr Vector3 VerticesWireframe[]{
{-1.0f, -1.0f, 0.0f},
{ 1.0f, -1.0f, 0.0f},
{ 1.0f, 1.0f, 0.0f},
{-1.0f, 1.0f, 0.0f}
};
constexpr Trade::MeshAttributeData AttributesWireframe[]{
Trade::MeshAttributeData{Trade::MeshAttribute::Position,
Containers::arrayView(VerticesWireframe)}
};
}
Trade::MeshData3D planeWireframe() {
return Trade::MeshData3D{MeshPrimitive::LineLoop, {}, {{
{-1.0f, -1.0f, 0.0f},
{1.0f, -1.0f, 0.0f},
{1.0f, 1.0f, 0.0f},
{-1.0f, 1.0f, 0.0f}
}}, {}, {}, {}, nullptr};
Trade::MeshData planeWireframe() {
return Trade::MeshData{MeshPrimitive::LineLoop,
{}, VerticesWireframe,
Trade::meshAttributeDataNonOwningArray(AttributesWireframe)};
}
}}

12
src/Magnum/Primitives/Plane.h

@ -50,24 +50,28 @@ enum class PlaneTextureCoords: UnsignedByte {
@brief Solid 3D plane
2x2 plane. Non-indexed @ref MeshPrimitive::TriangleStrip on the XY plane with
normals in positive Z direction.
@ref VertexFormat::Vector3 positions and @ref VertexFormat::Vector3 normals in
positive Z direction. The returned instance references data stored in constant
memory.
@image html primitives-planesolid.png width=256px
@see @ref planeWireframe(), @ref squareSolid(), @ref gradient3D()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D planeSolid(PlaneTextureCoords textureCoords = PlaneTextureCoords::DontGenerate);
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData planeSolid(PlaneTextureCoords textureCoords = PlaneTextureCoords::DontGenerate);
/**
@brief Wireframe 3D plane
2x2 plane. Non-indexed @ref MeshPrimitive::LineLoop on the XY plane.
2x2 plane. Non-indexed @ref MeshPrimitive::LineLoop on the XY plane with
@ref VertexFormat::Vector3 positions. The returned instance references data
stored in constant memory.
@image html primitives-planewireframe.png width=256px
@see @ref planeSolid(), @ref squareWireframe()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D planeWireframe();
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData planeWireframe();
}}

86
src/Magnum/Primitives/Square.cpp

@ -27,34 +27,74 @@
#include "Magnum/Mesh.h"
#include "Magnum/Math/Color.h"
#include "Magnum/Trade/MeshData2D.h"
#include "Magnum/Trade/MeshData.h"
namespace Magnum { namespace Primitives {
Trade::MeshData2D squareSolid(const SquareTextureCoords textureCoords) {
std::vector<std::vector<Vector2>> coords;
if(textureCoords == SquareTextureCoords::Generate) coords.push_back({
{1.0f, 0.0f},
{1.0f, 1.0f},
{0.0f, 0.0f},
{0.0f, 1.0f}
});
return Trade::MeshData2D{MeshPrimitive::TriangleStrip, {}, {{
{1.0f, -1.0f},
{1.0f, 1.0f},
{-1.0f, -1.0f},
{-1.0f, 1.0f}
}}, std::move(coords), {}, nullptr};
namespace {
constexpr Vector2 VerticesSolid[] {
{ 1.0f, -1.0f},
{ 1.0f, 1.0f},
{-1.0f, -1.0f},
{-1.0f, 1.0f}
};
constexpr struct VertexSolidTextureCoords {
Vector2 position;
Vector2 textureCoords;
} VerticesSolidTextureCoords[] {
{{ 1.0f, -1.0f}, {1.0f, 0.0f}},
{{ 1.0f, 1.0f}, {1.0f, 1.0f}},
{{-1.0f, -1.0f}, {0.0f, 0.0f}},
{{-1.0f, 1.0f}, {0.0f, 1.0f}}
};
constexpr Trade::MeshAttributeData AttributesSolid[]{
Trade::MeshAttributeData{Trade::MeshAttribute::Position,
Containers::stridedArrayView(VerticesSolid)}
};
constexpr Trade::MeshAttributeData AttributesSolidTextureCoords[]{
Trade::MeshAttributeData{Trade::MeshAttribute::Position,
Containers::stridedArrayView(VerticesSolidTextureCoords,
&VerticesSolidTextureCoords[0].position,
Containers::arraySize(VerticesSolidTextureCoords), sizeof(VertexSolidTextureCoords))},
Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates,
Containers::stridedArrayView(VerticesSolidTextureCoords,
&VerticesSolidTextureCoords[0].textureCoords,
Containers::arraySize(VerticesSolidTextureCoords), sizeof(VertexSolidTextureCoords))}
};
}
Trade::MeshData squareSolid(const SquareTextureCoords textureCoords) {
if(textureCoords != SquareTextureCoords::Generate)
return Trade::MeshData{MeshPrimitive::TriangleStrip,
{}, VerticesSolid,
Trade::meshAttributeDataNonOwningArray(AttributesSolid)};
return Trade::MeshData{MeshPrimitive::TriangleStrip,
{}, VerticesSolidTextureCoords,
Trade::meshAttributeDataNonOwningArray(AttributesSolidTextureCoords)};
}
namespace {
constexpr Vector2 VerticesWireframe[]{
{-1.0f, -1.0f},
{ 1.0f, -1.0f},
{ 1.0f, 1.0f},
{-1.0f, 1.0f}
};
constexpr Trade::MeshAttributeData AttributesWireframe[]{
Trade::MeshAttributeData{Trade::MeshAttribute::Position,
Containers::stridedArrayView(VerticesWireframe)}
};
}
Trade::MeshData2D squareWireframe() {
return Trade::MeshData2D{MeshPrimitive::LineLoop, {}, {{
{-1.0f, -1.0f},
{1.0f, -1.0f},
{1.0f, 1.0f},
{-1.0f, 1.0f}
}}, {}, {}, nullptr};
Trade::MeshData squareWireframe() {
return Trade::MeshData{MeshPrimitive::LineLoop,
{}, VerticesWireframe,
Trade::meshAttributeDataNonOwningArray(AttributesWireframe)};
}
}}

13
src/Magnum/Primitives/Square.h

@ -49,24 +49,29 @@ enum class SquareTextureCoords: UnsignedByte {
/**
@brief Solid 2D square
2x2 square. Non-indexed @ref MeshPrimitive::TriangleStrip.
2x2 square. Non-indexed @ref MeshPrimitive::TriangleStrip with interleaved
@ref VertexFormat::Vector2 positions and optional @ref VertexFormat::Vector2
texture coordinates. The returned instance references data stored in constant
memory.
@image html primitives-squaresolid.png width=256px
@see @ref squareWireframe(), @ref planeSolid(), @ref gradient2D()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData2D squareSolid(SquareTextureCoords textureCoords = SquareTextureCoords::DontGenerate);
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData squareSolid(SquareTextureCoords textureCoords = SquareTextureCoords::DontGenerate);
/**
@brief Wireframe 2D square
2x2 square. Non-indexed @ref MeshPrimitive::LineLoop.
2x2 square. Non-indexed @ref MeshPrimitive::LineLoop with
@ref VertexFormat::Vector2 positions. The returned instance references data
stored in constant memory.
@image html primitives-squarewireframe.png width=256px
@see @ref squareSolid(), @ref planeWireframe()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData2D squareWireframe();
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData squareWireframe();
}}

10
src/Magnum/Primitives/UVSphere.cpp

@ -29,14 +29,14 @@
#include "Magnum/Math/Color.h"
#include "Magnum/Primitives/Implementation/Spheroid.h"
#include "Magnum/Primitives/Implementation/WireframeSpheroid.h"
#include "Magnum/Trade/MeshData3D.h"
#include "Magnum/Trade/MeshData.h"
namespace Magnum { namespace Primitives {
Trade::MeshData3D uvSphereSolid(UnsignedInt rings, UnsignedInt segments, UVSphereTextureCoords textureCoords) {
Trade::MeshData uvSphereSolid(UnsignedInt rings, UnsignedInt segments, UVSphereTextureCoords textureCoords) {
CORRADE_ASSERT(rings >= 2 && segments >= 3,
"Primitives::uvSphereSolid(): at least two rings and three segments expected",
(Trade::MeshData3D{MeshPrimitive::Triangles, {}, {}, {}, {}, {}, nullptr}));
(Trade::MeshData{MeshPrimitive::Triangles, 0}));
Implementation::Spheroid sphere(segments, textureCoords == UVSphereTextureCoords::Generate ?
Implementation::Spheroid::TextureCoords::Generate :
@ -62,10 +62,10 @@ Trade::MeshData3D uvSphereSolid(UnsignedInt rings, UnsignedInt segments, UVSpher
return sphere.finalize();
}
Trade::MeshData3D uvSphereWireframe(const UnsignedInt rings, const UnsignedInt segments) {
Trade::MeshData uvSphereWireframe(const UnsignedInt rings, const UnsignedInt segments) {
CORRADE_ASSERT(rings >= 2 && rings%2 == 0 && segments >= 4 && segments%4 == 0,
"Primitives::uvSphereWireframe(): multiples of 2 rings and multiples of 4 segments expected",
(Trade::MeshData3D{MeshPrimitive::Triangles, {}, {}, {}, {}, {}, nullptr}));
(Trade::MeshData{MeshPrimitive::Triangles, 0}));
Implementation::WireframeSpheroid sphere(segments/4);

14
src/Magnum/Primitives/UVSphere.h

@ -52,15 +52,17 @@ enum class UVSphereTextureCoords: UnsignedByte {
equal to @cpp 3 @ce.
@param textureCoords Whether to generate texture coordinates
Sphere with radius @cpp 1.0f @ce. Indexed @ref MeshPrimitive::Triangles with
normals and optional 2D texture coordinates. If texture coordinates are
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.
@image html primitives-uvspheresolid.png width=256px
@see @ref icosphereSolid()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D uvSphereSolid(UnsignedInt rings, UnsignedInt segments, UVSphereTextureCoords textureCoords = UVSphereTextureCoords::DontGenerate);
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData uvSphereSolid(UnsignedInt rings, UnsignedInt segments, UVSphereTextureCoords textureCoords = UVSphereTextureCoords::DontGenerate);
/**
@brief Wireframe 3D UV sphere
@ -69,13 +71,15 @@ MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D uvSphereSolid(UnsignedInt rings, Unsi
@param segments Number of (line) segments. Must be larger or equal to
@cpp 4 @ce and multiple of @cpp 4 @ce.
Sphere with radius @cpp 1.0f @ce. Indexed @ref MeshPrimitive::Lines.
Sphere with radius @cpp 1.0f @ce. @ref MeshPrimitive::Lines with
@ref MeshIndexType::UnsignedInt indices and @ref VertexFormat::Vector3
positions.
@image html primitives-uvspherewireframe.png width=256px
@see @ref icosphereSolid()
*/
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData3D uvSphereWireframe(UnsignedInt rings, UnsignedInt segments);
MAGNUM_PRIMITIVES_EXPORT Trade::MeshData uvSphereWireframe(UnsignedInt rings, UnsignedInt segments);
}}

3
src/Magnum/Trade/MeshData.h

@ -312,7 +312,8 @@ Containers::Array<MeshAttributeData> MAGNUM_TRADE_EXPORT meshAttributeDataNonOwn
Provides access to mesh vertex and index data, together with additional
information such as primitive type. Populated instances of this class are
returned from @ref AbstractImporter::mesh().
returned from @ref AbstractImporter::mesh() and from particular functions in
the @ref Primitives library.
@section Trade-MeshData-usage Basic usage

Loading…
Cancel
Save