Browse Source

MeshTools: generateIndices() operating directly on a MeshData.

pull/430/head
Vladimír Vondruš 6 years ago
parent
commit
b785a8ac69
  1. 3
      doc/changelog.dox
  2. 6
      src/Magnum/MeshTools/Concatenate.h
  3. 67
      src/Magnum/MeshTools/GenerateIndices.cpp
  4. 45
      src/Magnum/MeshTools/GenerateIndices.h
  5. 166
      src/Magnum/MeshTools/Test/GenerateIndicesTest.cpp

3
doc/changelog.dox

@ -155,7 +155,8 @@ See also:
@ref MeshTools::generateTriangleStripIndices() and
@ref MeshTools::generateTriangleFanIndices() utilities for converting
various mesh types to plain indexed @ref MeshPrimitive::Lines and
@ref MeshPrimitive::Triangles
@ref MeshPrimitive::Triangles, as well as @ref MeshTools::generateIndices()
operating directly on a @ref Trade::MeshData
@subsubsection changelog-latest-new-platform Platform libraries

6
src/Magnum/MeshTools/Concatenate.h

@ -50,7 +50,11 @@ namespace Implementation {
The returned mesh contains vertices from all meshes concatenated together. If
any mesh is indexed, the resulting mesh is indexed as well, with indices
adjusted for vertex offsets of particular meshes. The behavior is undefined if
any mesh has indices out of bounds for its particular vertex count.
any mesh has indices out of bounds for its particular vertex count. Meshes with
@ref MeshPrimitive::LineStrip, @ref MeshPrimitive::LineLoop,
@ref MeshPrimitive::TriangleStrip and @ref MeshPrimitive::TriangleFan can't be
concatenated --- use @ref generateIndices() to turn them into
@ref MeshPrimitive::Lines or @ref MeshPrimitive::Triangles first.
All attributes from the @p first mesh are taken; for each mesh in @p next,
attributes present in @p first are copied, superfluous attributes ignored and

67
src/Magnum/MeshTools/GenerateIndices.cpp

@ -27,6 +27,9 @@
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/StridedArrayView.h>
#include <Corrade/Utility/Algorithms.h>
#include "Magnum/Trade/MeshData.h"
namespace Magnum { namespace MeshTools {
@ -143,4 +146,68 @@ Containers::Array<UnsignedInt> generateTriangleFanIndices(const UnsignedInt vert
return indices;
}
Trade::MeshData generateIndices(Trade::MeshData&& data) {
CORRADE_ASSERT(!data.isIndexed(),
"MeshTools::generateIndices(): mesh data already indexed",
(Trade::MeshData{MeshPrimitive::Triangles, 0}));
/* Transfer vertex / attribute data as-is, as those don't need any changes.
Release if possible. */
Containers::Array<char> vertexData;
const UnsignedInt vertexCount = data.vertexCount();
if(data.vertexDataFlags() & Trade::DataFlag::Owned)
vertexData = data.releaseVertexData();
else {
vertexData = Containers::Array<char>{Containers::NoInit, data.vertexData().size()};
Utility::copy(data.vertexData(), vertexData);
}
/* Recreate the attribute array with views on the new vertexData */
/** @todo if the vertex data were moved and this array is owned, it
wouldn't need to be recreated, but the logic is a bit complex */
Containers::Array<Trade::MeshAttributeData> attributeData{data.attributeCount()};
for(UnsignedInt i = 0, max = attributeData.size(); i != max; ++i) {
attributeData[i] = Trade::MeshAttributeData{data.attributeName(i),
data.attributeFormat(i),
Containers::StridedArrayView1D<const void>{vertexData, vertexData.data() + data.attributeOffset(i), vertexCount, data.attributeStride(i)}};
}
/* Generate the index array */
MeshPrimitive primitive;
Containers::Array<char> indexData;
if(data.primitive() == MeshPrimitive::LineStrip) {
primitive = MeshPrimitive::Lines;
indexData = Containers::Array<char>{Containers::NoInit, 2*(vertexCount - 1)*sizeof(UnsignedInt)};
generateLineStripIndicesInto(vertexCount, Containers::arrayCast<UnsignedInt>(indexData));
} else if(data.primitive() == MeshPrimitive::LineLoop) {
primitive = MeshPrimitive::Lines;
indexData = Containers::Array<char>{Containers::NoInit, 2*vertexCount*sizeof(UnsignedInt)};
generateLineLoopIndicesInto(vertexCount, Containers::arrayCast<UnsignedInt>(indexData));
} else if(data.primitive() == MeshPrimitive::TriangleStrip) {
primitive = MeshPrimitive::Triangles;
indexData = Containers::Array<char>{Containers::NoInit, 3*(vertexCount - 2)*sizeof(UnsignedInt)};
generateTriangleStripIndicesInto(vertexCount, Containers::arrayCast<UnsignedInt>(indexData));
} else if(data.primitive() == MeshPrimitive::TriangleFan) {
primitive = MeshPrimitive::Triangles;
indexData = Containers::Array<char>{Containers::NoInit, 3*(vertexCount - 2)*sizeof(UnsignedInt)};
generateTriangleFanIndicesInto(vertexCount, Containers::arrayCast<UnsignedInt>(indexData));
} else CORRADE_ASSERT(false,
"MeshTools::generateIndices(): invalid primitive" << data.primitive(),
(Trade::MeshData{MeshPrimitive::Triangles, 0}));
Trade::MeshIndexData indices{MeshIndexType::UnsignedInt, indexData};
return Trade::MeshData{primitive, std::move(indexData), indices,
std::move(vertexData), std::move(attributeData)};
}
Trade::MeshData generateIndices(const Trade::MeshData& data) {
CORRADE_ASSERT(!data.isIndexed(),
"MeshTools::generateIndices(): mesh data already indexed",
(Trade::MeshData{MeshPrimitive::Triangles, 0}));
return generateIndices(Trade::MeshData{data.primitive(),
{}, data.vertexData(), Trade::meshAttributeDataNonOwningArray(data.attributeData()),
data.vertexCount()});
}
}}

45
src/Magnum/MeshTools/GenerateIndices.h

@ -26,12 +26,13 @@
*/
/** @file
* @brief Function @ref Magnum::MeshTools::generateLineStripIndices(), @ref Magnum::MeshTools::generateLineStripIndicesInto(), @ref Magnum::MeshTools::generateLineLoopIndices(), @ref Magnum::MeshTools::generateLineLoopIndicesInto(), @ref Magnum::MeshTools::generateTriangleStripIndices(), @ref Magnum::MeshTools::generateTriangleStripIndicesInto(), @ref Magnum::MeshTools::generateTriangleFanIndices(), @ref Magnum::MeshTools::generateTriangleFanIndicesInto()
* @brief Function @ref Magnum::MeshTools::generateLineStripIndices(), @ref Magnum::MeshTools::generateLineStripIndicesInto(), @ref Magnum::MeshTools::generateLineLoopIndices(), @ref Magnum::MeshTools::generateLineLoopIndicesInto(), @ref Magnum::MeshTools::generateTriangleStripIndices(), @ref Magnum::MeshTools::generateTriangleStripIndicesInto(), @ref Magnum::MeshTools::generateTriangleFanIndices(), @ref Magnum::MeshTools::generateTriangleFanIndicesInto(), @ref Magnum::MeshTools::generateIndices()
* @m_since_latest
*/
#include "Magnum/Magnum.h"
#include "Magnum/MeshTools/visibility.h"
#include "Magnum/Trade/Trade.h"
namespace Magnum { namespace MeshTools {
@ -43,7 +44,8 @@ Can be used to convert a @ref MeshPrimitive::LineStrip mesh to
@ref MeshPrimitive::Lines. The @p vertexCount is expected to be at least
@cpp 2 @ce. Primitive restart is not supported.
@see @ref generateLineStripIndicesInto(), @ref generateLineLoopIndices(),
@ref generateTriangleStripIndices(), @ref generateTriangleFanIndices()
@ref generateTriangleStripIndices(), @ref generateTriangleFanIndices(),
@ref generateIndices()
*/
MAGNUM_MESHTOOLS_EXPORT Containers::Array<UnsignedInt> generateLineStripIndices(UnsignedInt vertexCount);
@ -66,7 +68,8 @@ Can be used to convert a @ref MeshPrimitive::LineLoop mesh to
@ref MeshPrimitive::Lines. The @p vertexCount is expected to be at least
@cpp 2 @ce. Primitive restart is not supported.
@see @ref generateLineLoopIndicesInto(), @ref generateLineStripIndices(),
@ref generateTriangleStripIndices(), @ref generateTriangleFanIndices()
@ref generateTriangleStripIndices(), @ref generateTriangleFanIndices(),
@ref generateIndices()
*/
MAGNUM_MESHTOOLS_EXPORT Containers::Array<UnsignedInt> generateLineLoopIndices(UnsignedInt vertexCount);
@ -89,7 +92,8 @@ Can be used to convert a @ref MeshPrimitive::TriangleStrip mesh to
@ref MeshPrimitive::Triangles. The @p vertexCount is expected to be at least
@cpp 3 @ce. Primitive restart is not supported.
@see @ref generateTriangleStripIndicesInto(), @ref generateLineStripIndices(),
@ref generateLineLoopIndices(), @ref generateTriangleFanIndices()
@ref generateLineLoopIndices(), @ref generateTriangleFanIndices(),
@ref generateIndices()
*/
MAGNUM_MESHTOOLS_EXPORT Containers::Array<UnsignedInt> generateTriangleStripIndices(UnsignedInt vertexCount);
@ -112,7 +116,8 @@ Can be used to convert a @ref MeshPrimitive::TriangleFan mesh to
@ref MeshPrimitive::Triangles. The @p vertexCount is expected to be at least
@cpp 3 @ce. Primitive restart is not supported.
@see @ref generateTriangleFanIndicesInto(), @ref generateLineStripIndices(),
@ref generateLineLoopIndices(), @ref generateTriangleStripIndices()
@ref generateLineLoopIndices(), @ref generateTriangleStripIndices(),
@ref generateIndices()
*/
MAGNUM_MESHTOOLS_EXPORT Containers::Array<UnsignedInt> generateTriangleFanIndices(UnsignedInt vertexCount);
@ -127,6 +132,36 @@ least @cpp 3 @ce, the @p indices array is expected to have a size of
*/
MAGNUM_MESHTOOLS_EXPORT void generateTriangleFanIndicesInto(UnsignedInt vertexCount, const Containers::StridedArrayView1D<UnsignedInt>& into);
/**
@brief Convert a mesh to plain indexed lines or triangles
@m_since_latest
Expects that @p mesh is not indexed and is one of
@ref MeshPrimitive::LineStrip, @ref MeshPrimitive::LineLoop,
@ref MeshPrimitive::TriangleStrip, @ref MeshPrimitive::TriangleFan primitives.
If your mesh is indexed, call @ref duplicate(const Trade::MeshData& data, Containers::ArrayView<const Trade::MeshAttributeData>)
on it first.
The resulting mesh always has @ref MeshIndexType::UnsignedInt, call
@ref compressIndices(const Trade::MeshData&, MeshIndexType) on the result to
compress it to a smaller type, if desired. This function will unconditionally
make a copy of all vertex data, use @ref generateIndices(Trade::MeshData&&) to
avoid that copy.
*/
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData generateIndices(const Trade::MeshData& mesh);
/**
@brief Convert a mesh to plain indexed lines or triangles
@m_since_latest
Compared to @ref generateIndices(const Trade::MeshData&) this function can
transfer ownership of @p data vertex buffer (in case it is owned) to the
returned instance instead of making a copy of it. Attribute data is copied
always.
@see @ref Trade::MeshData::vertexDataFlags()
*/
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData generateIndices(Trade::MeshData&& data);
}}
#endif

166
src/Magnum/MeshTools/Test/GenerateIndicesTest.cpp

@ -30,7 +30,9 @@
#include <Corrade/TestSuite/Compare/Container.h>
#include <Corrade/Utility/DebugStl.h>
#include "Magnum/Math/Vector2.h"
#include "Magnum/MeshTools/GenerateIndices.h"
#include "Magnum/Trade/MeshData.h"
namespace Magnum { namespace MeshTools { namespace Test { namespace {
@ -52,6 +54,40 @@ struct GenerateIndicesTest: TestSuite::Tester {
void generateTriangleFanIndices();
void generateTriangleFanIndicesWrongVertexCount();
void generateTriangleFanIndicesIntoWrongSize();
void generateIndicesMeshData();
void generateIndicesMeshDataMove();
void generateIndicesMeshDataIndexed();
void generateIndicesMeshDataInvalidPrimitive();
};
const struct {
MeshPrimitive primitive;
Containers::Array<UnsignedInt> indices;
} MeshDataData[] {
{MeshPrimitive::LineStrip, Containers::array<UnsignedInt>({
0, 1,
1, 2,
2, 3,
3, 4
})},
{MeshPrimitive::LineLoop, Containers::array<UnsignedInt>({
0, 1,
1, 2,
2, 3,
3, 4,
4, 0
})},
{MeshPrimitive::TriangleStrip, Containers::array<UnsignedInt>({
0, 1, 2,
2, 1, 3, /* Reversed */
2, 3, 4
})},
{MeshPrimitive::TriangleFan, Containers::array<UnsignedInt>({
0, 1, 2,
0, 2, 3,
0, 3, 4
})}
};
GenerateIndicesTest::GenerateIndicesTest() {
@ -70,6 +106,13 @@ GenerateIndicesTest::GenerateIndicesTest() {
&GenerateIndicesTest::generateTriangleFanIndices,
&GenerateIndicesTest::generateTriangleFanIndicesWrongVertexCount,
&GenerateIndicesTest::generateTriangleFanIndicesIntoWrongSize});
addInstancedTests({&GenerateIndicesTest::generateIndicesMeshData},
Containers::arraySize(MeshDataData));
addTests({&GenerateIndicesTest::generateIndicesMeshDataMove,
&GenerateIndicesTest::generateIndicesMeshDataIndexed,
&GenerateIndicesTest::generateIndicesMeshDataInvalidPrimitive});
}
void GenerateIndicesTest::generateLineStripIndices() {
@ -259,6 +302,129 @@ void GenerateIndicesTest::generateTriangleFanIndicesIntoWrongSize() {
"MeshTools::generateTriangleFanIndicesInto(): bad output size, expected 9 but got 8\n");
}
void GenerateIndicesTest::generateIndicesMeshData() {
auto&& data = MeshDataData[testCaseInstanceId()];
{
std::ostringstream out;
Debug{&out, Debug::Flag::NoNewlineAtTheEnd} << data.primitive;
setTestCaseDescription(out.str());
}
const struct Vertex {
Vector2 position;
Vector2 textureCoordinates;
} vertexData[] {
{{1.5f, 0.3f}, {0.2f, 0.8f}},
{{2.5f, 1.3f}, {0.3f, 0.7f}},
{{3.5f, 2.3f}, {0.4f, 0.6f}},
{{4.5f, 3.3f}, {0.5f, 0.5f}},
{{5.5f, 4.3f}, {0.6f, 0.4f}}
};
Trade::MeshData mesh{data.primitive,
{}, vertexData, {
Trade::MeshAttributeData{Trade::MeshAttribute::Position,
Containers::stridedArrayView(vertexData,
&vertexData[0].position, 5, sizeof(Vertex))},
Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates,
Containers::stridedArrayView(vertexData,
&vertexData[0].textureCoordinates, 5, sizeof(Vertex))}
}};
Trade::MeshData out = generateIndices(mesh);
CORRADE_VERIFY(out.isIndexed());
CORRADE_COMPARE(out.indexType(), MeshIndexType::UnsignedInt);
CORRADE_COMPARE_AS(out.indices<UnsignedInt>(), data.indices,
TestSuite::Compare::Container);
CORRADE_COMPARE(out.attributeCount(), 2);
CORRADE_COMPARE_AS(out.attribute<Vector2>(Trade::MeshAttribute::Position),
Containers::arrayView<Vector2>({
{1.5f, 0.3f}, {2.5f, 1.3f}, {3.5f, 2.3f}, {4.5f, 3.3f}, {5.5f, 4.3f}
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(out.attribute<Vector2>(Trade::MeshAttribute::TextureCoordinates),
Containers::arrayView<Vector2>({
{0.2f, 0.8f}, {0.3f, 0.7f}, {0.4f, 0.6f}, {0.5f, 0.5f}, {0.6f, 0.4f}
}), TestSuite::Compare::Container);
}
void GenerateIndicesTest::generateIndicesMeshDataMove() {
struct Vertex {
Vector2 position;
Vector2 textureCoordinates;
};
Containers::Array<char> vertexData{5*sizeof(Vertex)};
auto vertices = Containers::arrayCast<Vertex>(vertexData);
vertices[0].position = {1.5f, 0.3f};
vertices[1].position = {2.5f, 1.3f};
vertices[2].position = {3.5f, 2.3f};
vertices[3].position = {4.5f, 3.3f};
vertices[4].position = {5.5f, 4.3f};
vertices[0].textureCoordinates = {0.2f, 0.8f};
vertices[1].textureCoordinates = {0.3f, 0.7f};
vertices[2].textureCoordinates = {0.4f, 0.6f};
vertices[3].textureCoordinates = {0.5f, 0.5f};
vertices[4].textureCoordinates = {0.6f, 0.4f};
Trade::MeshData out = generateIndices(Trade::MeshData{
MeshPrimitive::TriangleFan, std::move(vertexData), {
Trade::MeshAttributeData{Trade::MeshAttribute::Position,
Containers::stridedArrayView(vertices,
&vertices[0].position, 5, sizeof(Vertex))},
Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates,
Containers::stridedArrayView(vertices,
&vertices[0].textureCoordinates, 5, sizeof(Vertex))}
}});
CORRADE_VERIFY(out.isIndexed());
CORRADE_COMPARE(out.indexType(), MeshIndexType::UnsignedInt);
CORRADE_COMPARE_AS(out.indices<UnsignedInt>(),
Containers::arrayView<UnsignedInt>({
0, 1, 2,
0, 2, 3,
0, 3, 4
}), TestSuite::Compare::Container);
CORRADE_COMPARE(out.attributeCount(), 2);
CORRADE_COMPARE_AS(out.attribute<Vector2>(Trade::MeshAttribute::Position),
Containers::arrayView<Vector2>({
{1.5f, 0.3f}, {2.5f, 1.3f}, {3.5f, 2.3f}, {4.5f, 3.3f}, {5.5f, 4.3f}
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(out.attribute<Vector2>(Trade::MeshAttribute::TextureCoordinates),
Containers::arrayView<Vector2>({
{0.2f, 0.8f}, {0.3f, 0.7f}, {0.4f, 0.6f}, {0.5f, 0.5f}, {0.6f, 0.4f}
}), TestSuite::Compare::Container);
/* The vertex data should be moved, not copied */
CORRADE_COMPARE(out.vertexData().data(), static_cast<void*>(vertices.data()));
}
void GenerateIndicesTest::generateIndicesMeshDataIndexed() {
UnsignedByte indices[]{0};
Trade::MeshData mesh{MeshPrimitive::TriangleFan,
{}, indices, Trade::MeshIndexData{indices}, 0};
/* Test both r-value and l-value overload */
std::ostringstream out;
Error redirectError{&out};
generateIndices(mesh);
generateIndices(Trade::MeshData{MeshPrimitive::TriangleFan,
{}, indices, Trade::MeshIndexData{indices}, 0});
CORRADE_COMPARE(out.str(),
"MeshTools::generateIndices(): mesh data already indexed\n"
"MeshTools::generateIndices(): mesh data already indexed\n");
}
void GenerateIndicesTest::generateIndicesMeshDataInvalidPrimitive() {
Trade::MeshData mesh{MeshPrimitive::Triangles, 2};
std::ostringstream out;
Error redirectError{&out};
generateIndices(mesh);
CORRADE_COMPARE(out.str(),
"MeshTools::generateIndices(): invalid primitive MeshPrimitive::Triangles\n");
}
}}}}
CORRADE_TEST_MAIN(Magnum::MeshTools::Test::GenerateIndicesTest)

Loading…
Cancel
Save