diff --git a/doc/generated/primitives.cpp b/doc/generated/primitives.cpp index ce4569342..300c4da29 100644 --- a/doc/generated/primitives.cpp +++ b/doc/generated/primitives.cpp @@ -452,7 +452,7 @@ std::pair PrimitiveVisualizer::gradient3DVertica } std::pair 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 PrimitiveVisualizer::crosshair2D() { } std::pair 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 PrimitiveVisualizer::squareWireframe() } std::pair 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 PrimitiveVisualizer::grid3DWireframe() } std::pair 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 PrimitiveVisualizer::squareSolid() { } std::pair 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"}; } diff --git a/doc/snippets/MagnumPrimitives.cpp b/doc/snippets/MagnumPrimitives.cpp index fcac06d21..7b86926d8 100644 --- a/doc/snippets/MagnumPrimitives.cpp +++ b/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; diff --git a/src/Magnum/Primitives/Axis.cpp b/src/Magnum/Primitives/Axis.cpp index 500a4ed09..e18f89351 100644 --- a/src/Magnum/Primitives/Axis.cpp +++ b/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)}; } }} diff --git a/src/Magnum/Primitives/Axis.h b/src/Magnum/Primitives/Axis.h index 169cd4476..5c25f6547 100644 --- a/src/Magnum/Primitives/Axis.h +++ b/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(); }} diff --git a/src/Magnum/Primitives/Capsule.cpp b/src/Magnum/Primitives/Capsule.cpp index 53ebed1af..e52e08881 100644 --- a/src/Magnum/Primitives/Capsule.cpp +++ b/src/Magnum/Primitives/Capsule.cpp @@ -25,28 +25,29 @@ #include "Capsule.h" +#include + #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 positions; - positions.reserve(hemisphereRings*4+2+(cylinderRings-1)*2); + Containers::Array 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 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 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 indices; - indices.reserve(hemisphereRings*8+cylinderRings*4); + Containers::Array 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(std::move(indexData)), indices, + Containers::arrayAllocatorCast(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); diff --git a/src/Magnum/Primitives/Capsule.h b/src/Magnum/Primitives/Capsule.h index fbc53ffd3..b94e465a9 100644 --- a/src/Magnum/Primitives/Capsule.h +++ b/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); }} diff --git a/src/Magnum/Primitives/Circle.cpp b/src/Magnum/Primitives/Circle.cpp index d38df8ded..61ddb4665 100644 --- a/src/Magnum/Primitives/Circle.cpp +++ b/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 positions; - positions.reserve(segments + 2); - - std::vector> 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 vertexData{stride*(segments + 2)}; + Containers::Array attributes{attributeCount}; + + /* Fill positions */ + Containers::StridedArrayView1D positions{vertexData, + reinterpret_cast(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 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 textureCoords{vertexData, + reinterpret_cast(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 positions; - positions.reserve(segments); + Containers::Array vertexData{segments*sizeof(Vector2)}; + auto positions = Containers::arrayCast(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 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 positions; - positions.reserve(segments + 2); - - std::vector> 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 vertexData{stride*(segments + 2)}; + Containers::Array attributes{attributeCount}; + + /* Fill positions */ + Containers::StridedArrayView1D positions{vertexData, + reinterpret_cast(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 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 normals{segments + 2, Vector3::zAxis(1.0f)}; + /* Fill normals */ + Containers::StridedArrayView1D normals{vertexData, + reinterpret_cast(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 textureCoords{vertexData, + reinterpret_cast(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 positions; - positions.reserve(segments); + Containers::Array vertexData{segments*sizeof(Vector3)}; + auto positions = Containers::arrayCast(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 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}}}; } }} diff --git a/src/Magnum/Primitives/Circle.h b/src/Magnum/Primitives/Circle.h index e8fcd8b3d..6a0eb90f7 100644 --- a/src/Magnum/Primitives/Circle.h +++ b/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); }} diff --git a/src/Magnum/Primitives/Cone.cpp b/src/Magnum/Primitives/Cone.cpp index 856b6c976..af573eec6 100644 --- a/src/Magnum/Primitives/Cone.cpp +++ b/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); diff --git a/src/Magnum/Primitives/Cone.h b/src/Magnum/Primitives/Cone.h index dcaab335b..9c147b8b9 100644 --- a/src/Magnum/Primitives/Cone.h +++ b/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); }} diff --git a/src/Magnum/Primitives/Crosshair.cpp b/src/Magnum/Primitives/Crosshair.cpp index eed1fefe1..0b1b30751 100644 --- a/src/Magnum/Primitives/Crosshair.cpp +++ b/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)}; } }} diff --git a/src/Magnum/Primitives/Crosshair.h b/src/Magnum/Primitives/Crosshair.h index 72151394d..873dda2eb 100644 --- a/src/Magnum/Primitives/Crosshair.h +++ b/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(); }} diff --git a/src/Magnum/Primitives/Cube.cpp b/src/Magnum/Primitives/Cube.cpp index b5d8c4716..1929ee70c 100644 --- a/src/Magnum/Primitives/Cube.cpp +++ b/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)}; } }} diff --git a/src/Magnum/Primitives/Cube.h b/src/Magnum/Primitives/Cube.h index 4e31f7f69..14e42e266 100644 --- a/src/Magnum/Primitives/Cube.h +++ b/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(); }} diff --git a/src/Magnum/Primitives/Cylinder.cpp b/src/Magnum/Primitives/Cylinder.cpp index 640f91f48..060c1bce4 100644 --- a/src/Magnum/Primitives/Cylinder.cpp +++ b/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); diff --git a/src/Magnum/Primitives/Cylinder.h b/src/Magnum/Primitives/Cylinder.h index 70a408028..0a57b95ba 100644 --- a/src/Magnum/Primitives/Cylinder.h +++ b/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); }} diff --git a/src/Magnum/Primitives/Gradient.cpp b/src/Magnum/Primitives/Gradient.cpp index 074ef78d7..a4931453b 100644 --- a/src/Magnum/Primitives/Gradient.cpp +++ b/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 positions{Vector2{ 1.0f, -1.0f}, - Vector2{ 1.0f, 1.0f}, - Vector2{-1.0f, -1.0f}, - Vector2{-1.0f, 1.0f}}; - std::vector 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 vertexData{sizeof(Vertex)*4}; + auto vertices = Containers::arrayCast(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 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 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 vertexData{sizeof(Vertex)*4}; + auto vertices = Containers::arrayCast(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); } diff --git a/src/Magnum/Primitives/Gradient.h b/src/Magnum/Primitives/Gradient.h index 0a8780ed4..b39160334 100644 --- a/src/Magnum/Primitives/Gradient.h +++ b/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); }} diff --git a/src/Magnum/Primitives/Grid.cpp b/src/Magnum/Primitives/Grid.cpp index 498d42d0d..d85262df8 100644 --- a/src/Magnum/Primitives/Grid.cpp +++ b/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 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 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 indexData{std::size_t(faceCount.product()*6)*sizeof(UnsignedInt)}; + auto indices = Containers::arrayCast(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> 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 vertexData{stride*vertexCount.product()}; + Containers::Array attributes{attributeCount}; + std::size_t attributeIndex = 0; + std::size_t attributeOffset = 0; + + /* Fill positions */ + Containers::StridedArrayView1D positions{vertexData, + reinterpret_cast(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 normals{vertexData, + reinterpret_cast(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> textureCoordinates; if(flags & GridFlag::GenerateTextureCoords) { - textureCoordinates.emplace_back(); - textureCoordinates[0].reserve(positions.size()); + Containers::StridedArrayView1D textureCoords{vertexData, + reinterpret_cast(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 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 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 indexData{sizeof(UnsignedInt)* + (vertexCount.y()*(vertexCount.x() - 1)*2 + + vertexCount.x()*(vertexCount.y() - 1)*2)}; + auto indices = Containers::arrayCast(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 vertexData{sizeof(Vector3)*vertexCount.product()}; + auto positions = Containers::arrayCast(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}}}; } }} diff --git a/src/Magnum/Primitives/Grid.h b/src/Magnum/Primitives/Grid.h index 09f7b8779..60f524e07 100644 --- a/src/Magnum/Primitives/Grid.h +++ b/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); }} diff --git a/src/Magnum/Primitives/Icosphere.cpp b/src/Magnum/Primitives/Icosphere.cpp index 80ee71b66..2ee07eb88 100644 --- a/src/Magnum/Primitives/Icosphere.cpp +++ b/src/Magnum/Primitives/Icosphere.cpp @@ -25,62 +25,103 @@ #include "Icosphere.h" +#include + #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 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 indexData{indexCount*sizeof(UnsignedInt)}; + auto indices = Containers::arrayCast(indexData); + std::memcpy(indices.begin(), Indices, sizeof(Indices)); - std::vector 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 vertexData; + arrayResize(vertexData, Containers::NoInit, sizeof(Vertex)*vertexCount); + + /* Build up the subdivided positions */ + { + auto vertices = Containers::arrayCast(vertexData); + Containers::StridedArrayView1D 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(vertexData); + Containers::StridedArrayView1D positions{vertices, &vertices[0].position, vertices.size(), sizeof(Vertex)}; + Containers::StridedArrayView1D 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 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}}}; } }} diff --git a/src/Magnum/Primitives/Icosphere.h b/src/Magnum/Primitives/Icosphere.h index b1bceb884..9a7981cf1 100644 --- a/src/Magnum/Primitives/Icosphere.h +++ b/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); }} diff --git a/src/Magnum/Primitives/Implementation/Spheroid.cpp b/src/Magnum/Primitives/Implementation/Spheroid.cpp index 9af9a762d..a1c89a869 100644 --- a/src/Magnum/Primitives/Implementation/Spheroid.cpp +++ b/src/Magnum/Primitives/Implementation/Spheroid.cpp @@ -25,25 +25,59 @@ #include "Spheroid.h" +#include + #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(Containers::arrayView(v))); + } else { + const Vertex v[]{{position, normal}}; + arrayAppend(_vertexData, Containers::arrayCast(Containers::arrayView(v))); + } +} + +void Spheroid::setLastVertexTextureCoords(const Vector2& textureCoords) { + /* Assuming append() was called before */ + Containers::arrayCast(_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 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 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(_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 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(_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 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(_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::move(textureCoords2D)} : std::vector>(), {}, 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(_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 attributes; + if(_textureCoords == TextureCoords::Generate) { + Trade::MeshAttributeData textureCoords{Trade::MeshAttribute::TextureCoordinates, + Containers::stridedArrayView(_vertexData, &typedVertices[0].textureCoords, + size, stride)}; + attributes = Containers::Array{Containers::InPlaceInit, {positions, normals, textureCoords}}; + } else { + attributes = Containers::Array{Containers::InPlaceInit, {positions, normals}}; + } + + return Trade::MeshData{MeshPrimitive::Triangles, + Containers::arrayAllocatorCast(std::move(_indexData)), indices, + std::move(_vertexData), std::move(attributes)}; } }}} diff --git a/src/Magnum/Primitives/Implementation/Spheroid.h b/src/Magnum/Primitives/Implementation/Spheroid.h index 4162f716a..62f03afe9 100644 --- a/src/Magnum/Primitives/Implementation/Spheroid.h +++ b/src/Magnum/Primitives/Implementation/Spheroid.h @@ -25,9 +25,11 @@ DEALINGS IN THE SOFTWARE. */ -#include +#include +#include #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 indices; - std::vector positions; - std::vector normals; - std::vector textureCoords2D; + Containers::Array _indexData; + Containers::Array _vertexData; + + void append(const Vector3& position, const Vector3& normal, const Vector2& textureCoords = {}); + void setLastVertexTextureCoords(const Vector2& textureCoords); }; }}} diff --git a/src/Magnum/Primitives/Implementation/WireframeSpheroid.cpp b/src/Magnum/Primitives/Implementation/WireframeSpheroid.cpp index 64dccafca..6b40ca3aa 100644 --- a/src/Magnum/Primitives/Implementation/WireframeSpheroid.cpp +++ b/src/Magnum/Primitives/Implementation/WireframeSpheroid.cpp @@ -25,24 +25,26 @@ #include "WireframeSpheroid.h" +#include + #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 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 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 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(std::move(_indexData)), indices, + Containers::arrayAllocatorCast(std::move(_vertexData)), {positions}}; } }}} diff --git a/src/Magnum/Primitives/Implementation/WireframeSpheroid.h b/src/Magnum/Primitives/Implementation/WireframeSpheroid.h index 24bd3d239..60042bcc7 100644 --- a/src/Magnum/Primitives/Implementation/WireframeSpheroid.h +++ b/src/Magnum/Primitives/Implementation/WireframeSpheroid.h @@ -25,7 +25,7 @@ DEALINGS IN THE SOFTWARE. */ -#include +#include #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 _indices; - std::vector _positions; + Containers::Array _indexData; + Containers::Array _vertexData; }; }}} diff --git a/src/Magnum/Primitives/Line.cpp b/src/Magnum/Primitives/Line.cpp index 2f78cba96..37a05460a 100644 --- a/src/Magnum/Primitives/Line.cpp +++ b/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 vertexData{sizeof(Vector2)*2}; + auto positions = Containers::arrayCast(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 vertexData{sizeof(Vector3)*2}; + auto positions = Containers::arrayCast(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}); } diff --git a/src/Magnum/Primitives/Line.h b/src/Magnum/Primitives/Line.h index 7bc9863d6..8c48eccfb 100644 --- a/src/Magnum/Primitives/Line.h +++ b/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(); }} diff --git a/src/Magnum/Primitives/Plane.cpp b/src/Magnum/Primitives/Plane.cpp index 48797668a..a6cd27a9b 100644 --- a/src/Magnum/Primitives/Plane.cpp +++ b/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> 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)}; } }} diff --git a/src/Magnum/Primitives/Plane.h b/src/Magnum/Primitives/Plane.h index 0f2cab9cd..2bb902b81 100644 --- a/src/Magnum/Primitives/Plane.h +++ b/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(); }} diff --git a/src/Magnum/Primitives/Square.cpp b/src/Magnum/Primitives/Square.cpp index 93c833e95..82dd8165b 100644 --- a/src/Magnum/Primitives/Square.cpp +++ b/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> 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)}; } }} diff --git a/src/Magnum/Primitives/Square.h b/src/Magnum/Primitives/Square.h index 4e0940dc9..5db993e92 100644 --- a/src/Magnum/Primitives/Square.h +++ b/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(); }} diff --git a/src/Magnum/Primitives/UVSphere.cpp b/src/Magnum/Primitives/UVSphere.cpp index 597310027..b4434d251 100644 --- a/src/Magnum/Primitives/UVSphere.cpp +++ b/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); diff --git a/src/Magnum/Primitives/UVSphere.h b/src/Magnum/Primitives/UVSphere.h index 24c595905..ccec5c2d7 100644 --- a/src/Magnum/Primitives/UVSphere.h +++ b/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); }} diff --git a/src/Magnum/Trade/MeshData.h b/src/Magnum/Trade/MeshData.h index c710d4aa3..3409e5ddc 100644 --- a/src/Magnum/Trade/MeshData.h +++ b/src/Magnum/Trade/MeshData.h @@ -312,7 +312,8 @@ Containers::Array 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