From c8de337c061c506801446521f5061f17e9223f5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 2 Dec 2019 01:38:23 +0100 Subject: [PATCH] Primitives: port away from MeshDataXD. The internals don't use any std::vector anymore, only the icosphere needs an std::unordered_map to do duplicate removal. Additionally, the most simple primitives are now simply views on constant data, being completely zero-allocation. On a Mac this resulted in the dylib going down from 1.5 MB to 418 kB in Debug, and from 129 kB to 90 kB in Release. Quite nice. The tests are not ported away from MeshDataXD yet as I want to ensure the behavior is *exactly* as before. --- doc/generated/primitives.cpp | 10 +- doc/snippets/MagnumPrimitives.cpp | 3 +- src/Magnum/Primitives/Axis.cpp | 160 +++++++------ src/Magnum/Primitives/Axis.h | 16 +- src/Magnum/Primitives/Capsule.cpp | 56 +++-- src/Magnum/Primitives/Capsule.h | 25 +- src/Magnum/Primitives/Circle.cpp | 150 +++++++----- src/Magnum/Primitives/Circle.h | 22 +- src/Magnum/Primitives/Cone.cpp | 10 +- src/Magnum/Primitives/Cone.h | 23 +- src/Magnum/Primitives/Crosshair.cpp | 41 +++- src/Magnum/Primitives/Crosshair.h | 12 +- src/Magnum/Primitives/Cube.cpp | 224 ++++++++++-------- src/Magnum/Primitives/Cube.h | 22 +- src/Magnum/Primitives/Cylinder.cpp | 10 +- src/Magnum/Primitives/Cylinder.h | 24 +- src/Magnum/Primitives/Gradient.cpp | 86 ++++--- src/Magnum/Primitives/Gradient.h | 28 ++- src/Magnum/Primitives/Grid.cpp | 159 ++++++++----- src/Magnum/Primitives/Grid.h | 19 +- src/Magnum/Primitives/Icosphere.cpp | 131 ++++++---- src/Magnum/Primitives/Icosphere.h | 7 +- .../Primitives/Implementation/Spheroid.cpp | 220 +++++++++++------ .../Primitives/Implementation/Spheroid.h | 19 +- .../Implementation/WireframeSpheroid.cpp | 64 +++-- .../Implementation/WireframeSpheroid.h | 8 +- src/Magnum/Primitives/Line.cpp | 27 ++- src/Magnum/Primitives/Line.h | 14 +- src/Magnum/Primitives/Plane.cpp | 105 +++++--- src/Magnum/Primitives/Plane.h | 12 +- src/Magnum/Primitives/Square.cpp | 86 +++++-- src/Magnum/Primitives/Square.h | 13 +- src/Magnum/Primitives/UVSphere.cpp | 10 +- src/Magnum/Primitives/UVSphere.h | 14 +- src/Magnum/Trade/MeshData.h | 3 +- 35 files changed, 1137 insertions(+), 696 deletions(-) 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