Browse Source

Trade: implement mutable data access in MeshData.

Turns out the design wasn't so simple after all. AnimationData and
ImageData classes will follow with similar changes.
pull/371/head
Vladimír Vondruš 7 years ago
parent
commit
8ea86b05f9
  1. 15
      doc/snippets/MagnumTrade.cpp
  2. 4
      src/Magnum/Trade/Data.cpp
  3. 10
      src/Magnum/Trade/Data.h
  4. 5
      src/Magnum/Trade/Implementation/arrayUtilities.h
  5. 62
      src/Magnum/Trade/MeshData.cpp
  6. 287
      src/Magnum/Trade/MeshData.h
  7. 403
      src/Magnum/Trade/Test/MeshDataTest.cpp

15
doc/snippets/MagnumTrade.cpp

@ -266,6 +266,21 @@ if(data.isIndexed()) {
}
#endif
{
Trade::MeshData data{MeshPrimitive::Points, 0};
/* [MeshData-usage-mutable] */
/* Check prerequisites */
if(!(data.vertexDataFlags() & Trade::DataFlag::Mutable) ||
!data.hasAttribute(Trade::MeshAttribute::Position) ||
data.attributeFormat(Trade::MeshAttribute::Position) != VertexFormat::Vector3)
Fatal{} << "Oh well";
/* Scale the mesh two times */
MeshTools::transformPointsInPlace(Matrix4::scaling(Vector3{2.0f}),
data.mutableAttribute<Vector3>(Trade::MeshAttribute::Position));
/* [MeshData-usage-mutable] */
}
{
Trade::MeshData2D& foo();
Trade::MeshData2D& data = foo();

4
src/Magnum/Trade/Data.cpp

@ -50,4 +50,8 @@ Debug& operator<<(Debug& debug, const DataFlags value) {
DataFlag::Mutable});
}
namespace Implementation {
void nonOwnedArrayDeleter(char*, std::size_t) { /* does nothing */ }
}
}}

10
src/Magnum/Trade/Data.h

@ -41,7 +41,8 @@ namespace Magnum { namespace Trade {
@brief Data flag
@m_since_latest
@see @ref DataFlags, @ref MeshData::dataFlags()
@see @ref DataFlags, @ref MeshData::indexDataFlags(),
@ref MeshData::vertexDataFlags()
*/
enum class DataFlag: UnsignedByte {
/**
@ -70,7 +71,7 @@ MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, DataFlag value);
@brief Data flags
@m_since_latest
@see @ref MeshData::dataFlags()
@see @ref MeshData::indexDataFlags(), @ref MeshData::vertexDataFlags()
*/
typedef Containers::EnumSet<DataFlag> DataFlags;
@ -82,6 +83,11 @@ CORRADE_ENUMSET_OPERATORS(DataFlags)
*/
MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, DataFlags value);
namespace Implementation {
/* Used internally by MeshData */
MAGNUM_TRADE_EXPORT void nonOwnedArrayDeleter(char*, std::size_t);
}
}}
#endif

5
src/Magnum/Trade/Implementation/arrayUtilities.h

@ -32,9 +32,8 @@
namespace Magnum { namespace Trade { namespace Implementation {
/* Can't use InPlaceInit as that uses a custom deleters. Compared to
InPlaceInit it does an an unnecessary default-initialization of all
elements */
/* Can't use InPlaceInit as that uses custom deleters. Compared to InPlaceInit
it does an an unnecessary default-initialization of all elements. */
/** @todo isn't there some C++56 feature that would allow me to allocate
without calling constructors? */
template<class T> Containers::Array<T> initializerListToArrayWithDefaultDeleter(const std::initializer_list<T> list) {

62
src/Magnum/Trade/MeshData.cpp

@ -58,7 +58,12 @@ MeshAttributeData::MeshAttributeData(const MeshAttribute name, const VertexForma
"Trade::MeshAttributeData:" << format << "is not a valid format for" << name, );
}
MeshData::MeshData(const MeshPrimitive primitive, Containers::Array<char>&& indexData, const MeshIndexData& indices, Containers::Array<char>&& vertexData, Containers::Array<MeshAttributeData>&& attributes, const void* const importerState) noexcept: _indexType{indices.type}, _primitive{primitive}, _importerState{importerState}, _indexData{std::move(indexData)}, _vertexData{std::move(vertexData)}, _attributes{std::move(attributes)}, _indices{indices.data} {
Containers::Array<MeshAttributeData> meshAttributeDataNonOwningArray(const Containers::ArrayView<const MeshAttributeData> view) {
/* Ugly, eh? */
return Containers::Array<Trade::MeshAttributeData>{const_cast<Trade::MeshAttributeData*>(view.data()), view.size(), reinterpret_cast<void(*)(Trade::MeshAttributeData*, std::size_t)>(Trade::Implementation::nonOwnedArrayDeleter)};
}
MeshData::MeshData(const MeshPrimitive primitive, Containers::Array<char>&& indexData, const MeshIndexData& indices, Containers::Array<char>&& vertexData, Containers::Array<MeshAttributeData>&& attributes, const void* const importerState) noexcept: _indexType{indices.type}, _primitive{primitive}, _indexDataFlags{DataFlag::Owned|DataFlag::Mutable}, _vertexDataFlags{DataFlag::Owned|DataFlag::Mutable}, _importerState{importerState}, _indexData{std::move(indexData)}, _vertexData{std::move(vertexData)}, _attributes{std::move(attributes)}, _indices{indices.data} {
/* Save vertex count. It's a strided array view, so the size is not
depending on type. */
if(_attributes.empty()) {
@ -92,13 +97,54 @@ MeshData::MeshData(const MeshPrimitive primitive, Containers::Array<char>&& inde
MeshData::MeshData(const MeshPrimitive primitive, Containers::Array<char>&& indexData, const MeshIndexData& indices, Containers::Array<char>&& vertexData, std::initializer_list<MeshAttributeData> attributes, const void* const importerState): MeshData{primitive, std::move(indexData), indices, std::move(vertexData), Implementation::initializerListToArrayWithDefaultDeleter(attributes), importerState} {}
MeshData::MeshData(const MeshPrimitive primitive, const DataFlags indexDataFlags, const Containers::ArrayView<const void> indexData, const MeshIndexData& indices, const DataFlags vertexDataFlags, const Containers::ArrayView<const void> vertexData, Containers::Array<MeshAttributeData>&& attributes, const void* const importerState) noexcept: MeshData{primitive, Containers::Array<char>{const_cast<char*>(static_cast<const char*>(indexData.data())), indexData.size(), Implementation::nonOwnedArrayDeleter}, indices, Containers::Array<char>{const_cast<char*>(static_cast<const char*>(vertexData.data())), vertexData.size(), Implementation::nonOwnedArrayDeleter}, std::move(attributes), importerState} {
CORRADE_ASSERT(!(indexDataFlags & DataFlag::Owned),
"Trade::MeshData: can't construct with non-owned index data but" << indexDataFlags, );
CORRADE_ASSERT(!(vertexDataFlags & DataFlag::Owned),
"Trade::MeshData: can't construct with non-owned vertex data but" << vertexDataFlags, );
_indexDataFlags = indexDataFlags;
_vertexDataFlags = vertexDataFlags;
}
MeshData::MeshData(const MeshPrimitive primitive, const DataFlags indexDataFlags, const Containers::ArrayView<const void> indexData, const MeshIndexData& indices, const DataFlags vertexDataFlags, const Containers::ArrayView<const void> vertexData, const std::initializer_list<MeshAttributeData> attributes, const void* const importerState): MeshData{primitive, indexDataFlags, indexData, indices, vertexDataFlags, vertexData, Implementation::initializerListToArrayWithDefaultDeleter(attributes), importerState} {}
MeshData::MeshData(const MeshPrimitive primitive, const DataFlags indexDataFlags, const Containers::ArrayView<const void> indexData, const MeshIndexData& indices, Containers::Array<char>&& vertexData, Containers::Array<MeshAttributeData>&& attributes, const void* const importerState) noexcept: MeshData{primitive, Containers::Array<char>{const_cast<char*>(static_cast<const char*>(indexData.data())), indexData.size(), Implementation::nonOwnedArrayDeleter}, indices, std::move(vertexData), std::move(attributes), importerState} {
CORRADE_ASSERT(!(indexDataFlags & DataFlag::Owned),
"Trade::MeshData: can't construct with non-owned index data but" << indexDataFlags, );
_indexDataFlags = indexDataFlags;
}
MeshData::MeshData(const MeshPrimitive primitive, const DataFlags indexDataFlags, const Containers::ArrayView<const void> indexData, const MeshIndexData& indices, Containers::Array<char>&& vertexData, const std::initializer_list<MeshAttributeData> attributes, const void* const importerState): MeshData{primitive, indexDataFlags, indexData, indices, std::move(vertexData), Implementation::initializerListToArrayWithDefaultDeleter(attributes), importerState} {}
MeshData::MeshData(const MeshPrimitive primitive, Containers::Array<char>&& indexData, const MeshIndexData& indices, const DataFlags vertexDataFlags, Containers::ArrayView<const void> vertexData, Containers::Array<MeshAttributeData>&& attributes, const void* const importerState) noexcept: MeshData{primitive, std::move(indexData), indices, Containers::Array<char>{const_cast<char*>(static_cast<const char*>(vertexData.data())), vertexData.size(), Implementation::nonOwnedArrayDeleter}, std::move(attributes), importerState} {
CORRADE_ASSERT(!(vertexDataFlags & DataFlag::Owned),
"Trade::MeshData: can't construct with non-owned vertex data but" << vertexDataFlags, );
_vertexDataFlags = vertexDataFlags;
}
MeshData::MeshData(const MeshPrimitive primitive, Containers::Array<char>&& indexData, const MeshIndexData& indices, const DataFlags vertexDataFlags, const Containers::ArrayView<const void> vertexData, const std::initializer_list<MeshAttributeData> attributes, const void* const importerState): MeshData{primitive, std::move(indexData), indices, vertexDataFlags, vertexData, Implementation::initializerListToArrayWithDefaultDeleter(attributes), importerState} {}
MeshData::MeshData(const MeshPrimitive primitive, Containers::Array<char>&& vertexData, Containers::Array<MeshAttributeData>&& attributes, const void* const importerState) noexcept: MeshData{primitive, {}, MeshIndexData{}, std::move(vertexData), std::move(attributes), importerState} {}
MeshData::MeshData(const MeshPrimitive primitive, Containers::Array<char>&& vertexData, const std::initializer_list<MeshAttributeData> attributes, const void* const importerState): MeshData{primitive, std::move(vertexData), Implementation::initializerListToArrayWithDefaultDeleter(attributes), importerState} {}
MeshData::MeshData(const MeshPrimitive primitive, const DataFlags vertexDataFlags, Containers::ArrayView<const void> vertexData, Containers::Array<MeshAttributeData>&& attributes, const void* const importerState) noexcept: MeshData{primitive, Containers::Array<char>{const_cast<char*>(static_cast<const char*>(vertexData.data())), vertexData.size(), Implementation::nonOwnedArrayDeleter}, std::move(attributes), importerState} {
CORRADE_ASSERT(!(vertexDataFlags & DataFlag::Owned),
"Trade::MeshData: can't construct with non-owned vertex data but" << vertexDataFlags, );
_vertexDataFlags = vertexDataFlags;
}
MeshData::MeshData(const MeshPrimitive primitive, const DataFlags vertexDataFlags, Containers::ArrayView<const void> vertexData, std::initializer_list<MeshAttributeData> attributes, const void* const importerState): MeshData{primitive, vertexDataFlags, vertexData, Implementation::initializerListToArrayWithDefaultDeleter(attributes), importerState} {}
MeshData::MeshData(const MeshPrimitive primitive, Containers::Array<char>&& indexData, const MeshIndexData& indices, const void* const importerState) noexcept: MeshData{primitive, std::move(indexData), indices, {}, {}, importerState} {}
MeshData::MeshData(const MeshPrimitive primitive, const UnsignedInt vertexCount, const void* const importerState) noexcept: _vertexCount{vertexCount}, _indexType{}, _primitive{primitive}, _importerState{importerState} {}
MeshData::MeshData(const MeshPrimitive primitive, const DataFlags indexDataFlags, const Containers::ArrayView<const void> indexData, const MeshIndexData& indices, const void* const importerState) noexcept: MeshData{primitive, Containers::Array<char>{const_cast<char*>(static_cast<const char*>(indexData.data())), indexData.size(), Implementation::nonOwnedArrayDeleter}, indices, importerState} {
CORRADE_ASSERT(!(indexDataFlags & DataFlag::Owned),
"Trade::MeshData: can't construct with non-owned index data but" << indexDataFlags, );
_indexDataFlags = indexDataFlags;
}
MeshData::MeshData(const MeshPrimitive primitive, const UnsignedInt vertexCount, const void* const importerState) noexcept: _vertexCount{vertexCount}, _indexType{}, _primitive{primitive}, _indexDataFlags{DataFlag::Owned|DataFlag::Mutable}, _vertexDataFlags{DataFlag::Owned|DataFlag::Mutable}, _importerState{importerState} {}
MeshData::~MeshData() = default;
@ -106,6 +152,18 @@ MeshData::MeshData(MeshData&&) noexcept = default;
MeshData& MeshData::operator=(MeshData&&) noexcept = default;
Containers::ArrayView<char> MeshData::mutableIndexData() & {
CORRADE_ASSERT(_indexDataFlags & DataFlag::Mutable,
"Trade::MeshData::mutableIndexData(): index data not mutable", {});
return _indexData;
}
Containers::ArrayView<char> MeshData::mutableVertexData() & {
CORRADE_ASSERT(_vertexDataFlags & DataFlag::Mutable,
"Trade::MeshData::mutableVertexData(): vertex data not mutable", {});
return _vertexData;
}
UnsignedInt MeshData::indexCount() const {
CORRADE_ASSERT(isIndexed(),
"Trade::MeshData::indexCount(): the mesh is not indexed", {});

287
src/Magnum/Trade/MeshData.h

@ -35,8 +35,8 @@
#include "Magnum/Mesh.h"
#include "Magnum/VertexFormat.h"
#include "Magnum/Trade/Data.h"
#include "Magnum/Trade/Trade.h"
#include "Magnum/Trade/visibility.h"
namespace Magnum { namespace Trade {
@ -243,6 +243,16 @@ class MAGNUM_TRADE_EXPORT MeshAttributeData {
Containers::StridedArrayView1D<const char> data;
};
/** @relatesalso MeshAttributeData
@brief Create a non-owning array of @ref MeshAttributeData items
@m_since_latest
Useful when you have the attribute definitions statically defined (for example
when the vertex data themselves are already defined at compile time) and don't
want to allocate just to pass those to @ref MeshData.
*/
Containers::Array<MeshAttributeData> MAGNUM_TRADE_EXPORT meshAttributeDataNonOwningArray(Containers::ArrayView<const MeshAttributeData> view);
/**
@brief Mesh data
@m_since_latest
@ -275,6 +285,20 @@ the GPU know of the format and layout:
@snippet MagnumTrade.cpp MeshData-usage-advanced
@section Trade-MeshData-usage-mutable Mutable data access
The interfaces implicitly provide @cpp const @ce views on the contained index
and vertex data through the @ref indexData(), @ref vertexData(),
@ref indices() and @ref attribute() accessors. This is done because in general
case the data can also refer to a memory-mapped file or constant memory. In
cases when it's desirable to modify the data in-place, there's the
@ref mutableIndexData(), @ref mutableVertexData(), @ref mutableIndices() and
@ref mutableAttribute() set of functions. To use these, you need to check that
the data are mutable using @ref indexDataFlags() or @ref vertexDataFlags()
first. The following snippet applies a transformation to the mesh data:
@snippet MagnumTrade.cpp MeshData-usage-mutable
@see @ref AbstractImporter::mesh()
*/
class MAGNUM_TRADE_EXPORT MeshData {
@ -295,6 +319,12 @@ class MAGNUM_TRADE_EXPORT MeshData {
* index-less attribute-less mesh, use
* @ref MeshData(MeshPrimitive, UnsignedInt, const void*) to specify
* desired vertex count.
*
* The @ref indexDataFlags() / @ref vertexDataFlags() are implicitly
* set to a combination of @ref DataFlag::Owned and
* @ref DataFlag::Mutable. For non-owned data use the
* @ref MeshData(MeshPrimitive, DataFlags, Containers::ArrayView<const void>, const MeshIndexData&, DataFlags, Containers::ArrayView<const void>, Containers::Array<MeshAttributeData>&&, const void*)
* constructor or its variants instead.
*/
explicit MeshData(MeshPrimitive primitive, Containers::Array<char>&& indexData, const MeshIndexData& indices, Containers::Array<char>&& vertexData, Containers::Array<MeshAttributeData>&& attributes, const void* importerState = nullptr) noexcept;
@ -302,6 +332,77 @@ class MAGNUM_TRADE_EXPORT MeshData {
/* Not noexcept because allocation happens inside */
explicit MeshData(MeshPrimitive primitive, Containers::Array<char>&& indexData, const MeshIndexData& indices, Containers::Array<char>&& vertexData, std::initializer_list<MeshAttributeData> attributes, const void* importerState = nullptr);
/**
* @brief Construct indexed mesh data with non-owned index and vertex data
* @param primitive Primitive
* @param indexDataFlags Index data flags
* @param indexData View on index data
* @param indices Index data description
* @param vertexDataFlags Vertex data flags
* @param vertexData View on vertex data
* @param attributes Description of all vertex attribute data
* @param importerState Importer-specific state
*
* Compared to @ref MeshData(MeshPrimitive, Containers::Array<char>&&, const MeshIndexData&, Containers::Array<char>&&, Containers::Array<MeshAttributeData>&&, const void*)
* creates an instance that doesn't own the passed vertex and index
* data. The @p indexDataFlags / @p vertexDataFlags parameters can
* contain @ref DataFlag::Mutable to indicate the external data can be
* modified, and is expected to *not* have @ref DataFlag::Owned set.
*/
explicit MeshData(MeshPrimitive primitive, DataFlags indexDataFlags, Containers::ArrayView<const void> indexData, const MeshIndexData& indices, DataFlags vertexDataFlags, Containers::ArrayView<const void> vertexData, Containers::Array<MeshAttributeData>&& attributes, const void* importerState = nullptr) noexcept;
/** @overload */
/* Not noexcept because allocation happens inside */
explicit MeshData(MeshPrimitive primitive, DataFlags indexDataFlags, Containers::ArrayView<const void> indexData, const MeshIndexData& indices, DataFlags vertexDataFlags, Containers::ArrayView<const void> vertexData, std::initializer_list<MeshAttributeData> attributes, const void* importerState = nullptr);
/**
* @brief Construct indexed mesh data with non-owned index data
* @param primitive Primitive
* @param indexDataFlags Index data flags
* @param indexData View on index data
* @param indices Index data description
* @param vertexData Vertex data
* @param attributes Description of all vertex attribute data
* @param importerState Importer-specific state
*
* Compared to @ref MeshData(MeshPrimitive, Containers::Array<char>&&, const MeshIndexData&, Containers::Array<char>&&, Containers::Array<MeshAttributeData>&&, const void*)
* creates an instance that doesn't own the passed index data. The
* @p indexDataFlags parameter can contain @ref DataFlag::Mutable to
* indicate the external data can be modified, and is expected to *not*
* have @ref DataFlag::Owned set. The @ref vertexDataFlags() are
* implicitly set to a combination of @ref DataFlag::Owned and
* @ref DataFlag::Mutable.
*/
explicit MeshData(MeshPrimitive primitive, DataFlags indexDataFlags, Containers::ArrayView<const void> indexData, const MeshIndexData& indices, Containers::Array<char>&& vertexData, Containers::Array<MeshAttributeData>&& attributes, const void* importerState = nullptr) noexcept;
/** @overload */
/* Not noexcept because allocation happens inside */
explicit MeshData(MeshPrimitive primitive, DataFlags indexDataFlags, Containers::ArrayView<const void> indexData, const MeshIndexData& indices, Containers::Array<char>&& vertexData, std::initializer_list<MeshAttributeData> attributes, const void* importerState = nullptr);
/**
* @brief Construct indexed mesh data with non-owned vertex data
* @param primitive Primitive
* @param indexData Index data
* @param indices Index data description
* @param vertexDataFlags Vertex data flags
* @param vertexData View on vertex data
* @param attributes Description of all vertex attribute data
* @param importerState Importer-specific state
*
* Compared to @ref MeshData(MeshPrimitive, Containers::Array<char>&&, const MeshIndexData&, Containers::Array<char>&&, Containers::Array<MeshAttributeData>&&, const void*)
* creates an instance that doesn't own the passed vertex data. The
* @p vertexDataFlags parameter can contain @ref DataFlag::Mutable to
* indicate the external data can be modified, and is expected to *not*
* have @ref DataFlag::Owned set. The @ref indexDataFlags() are
* implicitly set to a combination of @ref DataFlag::Owned and
* @ref DataFlag::Mutable.
*/
explicit MeshData(MeshPrimitive primitive, Containers::Array<char>&& indexData, const MeshIndexData& indices, DataFlags vertexDataFlags, Containers::ArrayView<const void> vertexData, Containers::Array<MeshAttributeData>&& attributes, const void* importerState = nullptr) noexcept;
/** @overload */
/* Not noexcept because allocation happens inside */
explicit MeshData(MeshPrimitive primitive, Containers::Array<char>&& indexData, const MeshIndexData& indices, DataFlags vertexDataFlags, Containers::ArrayView<const void> vertexData, std::initializer_list<MeshAttributeData> attributes, const void* importerState = nullptr);
/**
* @brief Construct a non-indexed mesh data
* @param primitive Primitive
@ -311,6 +412,14 @@ class MAGNUM_TRADE_EXPORT MeshData {
*
* Same as calling @ref MeshData(MeshPrimitive, Containers::Array<char>&&, const MeshIndexData&, Containers::Array<char>&&, Containers::Array<MeshAttributeData>&&, const void*)
* with default-constructed @p indexData and @p indices arguments.
*
* The @ref vertexDataFlags() are implicitly set to a combination of
* @ref DataFlag::Owned and @ref DataFlag::Mutable. For consistency,
* the @ref indexDataFlags() are implicitly set to a combination of
* @ref DataFlag::Owned and @ref DataFlag::Mutable, even though there
* isn't any data to own or to mutate. For non-owned data use the
* @ref MeshData(MeshPrimitive, DataFlags, Containers::ArrayView<const void>, Containers::Array<MeshAttributeData>&&, const void*)
* constructor instead.
*/
explicit MeshData(MeshPrimitive primitive, Containers::Array<char>&& vertexData, Containers::Array<MeshAttributeData>&& attributes, const void* importerState = nullptr) noexcept;
@ -318,6 +427,29 @@ class MAGNUM_TRADE_EXPORT MeshData {
/* Not noexcept because allocation happens inside */
explicit MeshData(MeshPrimitive primitive, Containers::Array<char>&& vertexData, std::initializer_list<MeshAttributeData> attributes, const void* importerState = nullptr);
/**
* @brief Construct a non-owned non-indexed mesh data
* @param primitive Primitive
* @param vertexDataFlags Vertex data flags
* @param vertexData View on vertex data
* @param attributes Description of all vertex attribute data
* @param importerState Importer-specific state
*
* Compared to @ref MeshData(MeshPrimitive, Containers::Array<char>&&, Containers::Array<MeshAttributeData>&&, const void*)
* creates an instance that doesn't own the passed data. The
* @p vertexDataFlags parameter can contain @ref DataFlag::Mutable to
* indicate the external data can be modified, and is expected to *not*
* have @ref DataFlag::Owned set. For consistency, the
* @ref indexDataFlags() are implicitly set to a combination of
* @ref DataFlag::Owned and @ref DataFlag::Mutable, even though there
* isn't any data to own or to mutate.
*/
explicit MeshData(MeshPrimitive primitive, DataFlags vertexDataFlags, Containers::ArrayView<const void> vertexData, Containers::Array<MeshAttributeData>&& attributes, const void* importerState = nullptr) noexcept;
/** @overload */
/* Not noexcept because allocation happens inside */
explicit MeshData(MeshPrimitive primitive, DataFlags vertexDataFlags, Containers::ArrayView<const void> vertexData, std::initializer_list<MeshAttributeData> attributes, const void* importerState = nullptr);
/**
* @brief Construct an attribute-less indexed mesh data
* @param primitive Primitive
@ -331,16 +463,47 @@ class MAGNUM_TRADE_EXPORT MeshData {
* to create an index-less attribute-less mesh, use
* @ref MeshData(MeshPrimitive, UnsignedInt, const void*) to specify
* desired vertex count.
*
* The @ref indexDataFlags() are implicitly set to a combination of
* @ref DataFlag::Owned and @ref DataFlag::Mutable. For consistency,
* the @ref vertexDataFlags() are implicitly set to a combination of
* @ref DataFlag::Owned and @ref DataFlag::Mutable, even though there
* isn't any data to own or to mutate. For non-owned data use the
* @ref MeshData(MeshPrimitive, DataFlags, Containers::ArrayView<const void>, const MeshIndexData&, const void*)
* constructor instead.
*/
explicit MeshData(MeshPrimitive primitive, Containers::Array<char>&& indexData, const MeshIndexData& indices, const void* importerState = nullptr) noexcept;
/**
* @brief Construct a non-owned attribute-less indexed mesh data
* @param primitive Primitive
* @param indexDataFlags Index data flags
* @param indexData View on index data
* @param indices Index data description
* @param importerState Importer-specific state
*
* Compared to @ref MeshData(MeshPrimitive, Containers::Array<char>&&, const MeshIndexData&, const void*)
* creates an instance that doesn't own the passed data. The
* @p indexDataFlags parameter can contain @ref DataFlag::Mutable to
* indicate the external data can be modified, and is expected to *not*
* have @ref DataFlag::Owned set. For consistency, the
* @ref vertexDataFlags() are implicitly set to a combination of
* @ref DataFlag::Owned and @ref DataFlag::Mutable, even though there
* isn't any data to own or to mutate.
*/
explicit MeshData(MeshPrimitive primitive, DataFlags indexDataFlags, Containers::ArrayView<const void> indexData, const MeshIndexData& indices, const void* importerState = nullptr) noexcept;
/**
* @brief Construct an index-less attribute-less mesh data
* @param primitive Primitive
* @param vertexCount Desired count of vertices to draw
* @param importerState Importer-specific state
*
* Useful in case the drawing is fully driven by a shader.
* Useful in case the drawing is fully driven by a shader. For
* consistency, the @ref indexDataFlags() / @ref vertexDataFlags() are
* implicitly set to a combination of @ref DataFlag::Owned and
* @ref DataFlag::Mutable, even though there isn't any data to own or
* to mutate.
*/
explicit MeshData(MeshPrimitive primitive, UnsignedInt vertexCount, const void* importerState = nullptr) noexcept;
@ -358,6 +521,22 @@ class MAGNUM_TRADE_EXPORT MeshData {
/** @brief Move assignment */
MeshData& operator=(MeshData&&) noexcept;
/**
* @brief Index data flags
*
* @see @ref releaseIndexData(), @ref mutableIndexData(),
* @ref mutableIndices()
*/
DataFlags indexDataFlags() const { return _indexDataFlags; }
/**
* @brief Vertex data flags
*
* @see @ref releaseVertexData(), @ref mutableVertexData(),
* @ref mutableAttribute()
*/
DataFlags vertexDataFlags() const { return _vertexDataFlags; }
/** @brief Primitive */
MeshPrimitive primitive() const { return _primitive; }
@ -366,13 +545,25 @@ class MAGNUM_TRADE_EXPORT MeshData {
*
* Returns @cpp nullptr @ce if the mesh is not indexed.
* @see @ref isIndexed(), @ref indexCount(), @ref indexType(),
* @ref indices(), @ref releaseIndexData()
* @ref indices(), @ref mutableIndexData(), @ref releaseIndexData()
*/
Containers::ArrayView<const char> indexData() const & { return _indexData; }
/** @brief Taking a view to a r-value instance is not allowed */
Containers::ArrayView<const char> indexData() const && = delete;
/**
* @brief Mutable raw index data
*
* Like @ref indexData(), but returns a non-const view. Expects that
* the mesh is mutable.
* @see @ref indexDataFlags()
*/
Containers::ArrayView<char> mutableIndexData() &;
/** @brief Taking a view to a r-value instance is not allowed */
Containers::ArrayView<char> mutableIndexData() && = delete;
/**
* @brief Raw vertex data
*
@ -380,13 +571,25 @@ class MAGNUM_TRADE_EXPORT MeshData {
* the mesh has no attributes.
* @see @ref attributeCount(), @ref attributeName(),
* @ref attributeFormat(), @ref attribute(),
* @ref releaseVertexData()
* @ref mutableVertexData(), @ref releaseVertexData()
*/
Containers::ArrayView<const char> vertexData() const & { return _vertexData; }
/** @brief Taking a view to a r-value instance is not allowed */
Containers::ArrayView<const char> vertexData() const && = delete;
/**
* @brief Mutable raw vertex data
*
* Like @ref vertexData(), but returns a non-const view. Expects that
* the mesh is mutable.
* @see @ref vertexDataFlags()
*/
Containers::ArrayView<char> mutableVertexData() &;
/** @brief Taking a view to a r-value instance is not allowed */
Containers::ArrayView<char> mutableVertexData() && = delete;
/** @brief Whether the mesh is indexed */
bool isIndexed() const { return _indexType != MeshIndexType{}; }
@ -418,10 +621,19 @@ class MAGNUM_TRADE_EXPORT MeshData {
* @ref indicesAsArray() accessor to get indices converted to 32-bit,
* but note that such operation involves extra allocation and data
* conversion.
* @see @ref isIndexed(), @ref attribute()
* @see @ref isIndexed(), @ref attribute(), @ref mutableIndices()
*/
template<class T> Containers::ArrayView<const T> indices() const;
/**
* @brief Mutable mesh indices
*
* Like @ref indices() const, but returns a mutable view. Expects that
* the mesh is mutable.
* @see @ref indexDataFlags()
*/
template<class T> Containers::ArrayView<T> mutableIndices();
/**
* @brief Mesh vertex count
*
@ -548,10 +760,20 @@ class MAGNUM_TRADE_EXPORT MeshData {
* @ref colorsAsArray() accessors to get common attributes converted to
* usual types, but note that these operations involve extra allocation
* and data conversion.
* @see @ref attribute(MeshAttribute, UnsignedInt) const
* @see @ref attribute(MeshAttribute, UnsignedInt) const,
* @ref mutableAttribute(MeshAttribute, UnsignedInt)
*/
template<class T> Containers::StridedArrayView1D<const T> attribute(UnsignedInt id) const;
/**
* @brief Mutable data for given attribute array
*
* Like @ref attribute(UnsignedInt) const, but returns a mutable view.
* Expects that the mesh is mutable.
* @see @ref vertexDataFlags()
*/
template<class T> Containers::StridedArrayView1D<T> mutableAttribute(UnsignedInt id);
/**
* @brief Data for given named attribute array
*
@ -564,10 +786,20 @@ class MAGNUM_TRADE_EXPORT MeshData {
* accessors to get common attributes converted to usual types, but
* note that these operations involve extra data conversion and an
* allocation.
* @see @ref attribute(UnsignedInt) const
* @see @ref attribute(UnsignedInt) const,
* @ref mutableAttribute(MeshAttribute, UnsignedInt)
*/
template<class T> Containers::StridedArrayView1D<const T> attribute(MeshAttribute name, UnsignedInt id = 0) const;
/**
* @brief Mutable data for given named attribute array
*
* Like @ref attribute(MeshAttribute, UnsignedInt) const, but returns a
* mutable view. Expects that the mesh is mutable.
* @see @ref vertexDataFlags()
*/
template<class T> Containers::StridedArrayView1D<T> mutableAttribute(MeshAttribute name, UnsignedInt id = 0);
/**
* @brief Indices as 32-bit integers
*
@ -701,8 +933,10 @@ class MAGNUM_TRADE_EXPORT MeshData {
*
* Releases the ownership of the index data array and resets internal
* index-related state to default. The mesh then behaves like
* non-indexed.
* @see @ref indexData()
* non-indexed. Note that the returned array has a custom no-op deleter
* when the data are not owned by the mesh, and while the returned
* array type is mutable, the actual memory might be not.
* @see @ref indexData(), @ref indexDataFlags()
*/
Containers::Array<char> releaseIndexData();
@ -711,8 +945,10 @@ class MAGNUM_TRADE_EXPORT MeshData {
*
* Releases the ownership of the index data array and resets internal
* attribute-related state to default. The mesh then behaves like if
* it has no attributes.
* @see @ref vertexData()
* it has no attributes. Note that the returned array has a custom
* no-op deleter when the data are not owned by the mesh, and while the
* returned array type is mutable, the actual memory might be not.
* @see @ref vertexData(), @ref vertexDataFlags()
*/
Containers::Array<char> releaseVertexData();
@ -734,6 +970,7 @@ class MAGNUM_TRADE_EXPORT MeshData {
UnsignedInt _vertexCount;
MeshIndexType _indexType;
MeshPrimitive _primitive;
DataFlags _indexDataFlags, _vertexDataFlags;
const void* _importerState;
Containers::Array<char> _indexData, _vertexData;
Containers::Array<MeshAttributeData> _attributes;
@ -789,6 +1026,16 @@ template<class T> Containers::ArrayView<const T> MeshData::indices() const {
return Containers::arrayCast<const T>(_indices);
}
template<class T> Containers::ArrayView<T> MeshData::mutableIndices() {
CORRADE_ASSERT(_indexDataFlags & DataFlag::Mutable,
"Trade::MeshData::mutableIndices(): index data not mutable", {});
CORRADE_ASSERT(isIndexed(),
"Trade::MeshData::mutableIndices(): the mesh is not indexed", {});
CORRADE_ASSERT(Implementation::meshIndexTypeFor<T>() == _indexType,
"Trade::MeshData::mutableIndices(): improper type requested for" << _indexType, nullptr);
return Containers::arrayCast<T>(reinterpret_cast<Containers::ArrayView<char>&>(_indices));
}
template<class T> Containers::StridedArrayView1D<const T> MeshData::attribute(UnsignedInt id) const {
CORRADE_ASSERT(id < _attributes.size(),
"Trade::MeshData::attribute(): index" << id << "out of range for" << _attributes.size() << "attributes", nullptr);
@ -797,12 +1044,30 @@ template<class T> Containers::StridedArrayView1D<const T> MeshData::attribute(Un
return Containers::arrayCast<const T>(_attributes[id].data);
}
template<class T> Containers::StridedArrayView1D<T> MeshData::mutableAttribute(UnsignedInt id) {
CORRADE_ASSERT(_vertexDataFlags & DataFlag::Mutable,
"Trade::MeshData::mutableAttribute(): vertex data not mutable", {});
CORRADE_ASSERT(id < _attributes.size(),
"Trade::MeshData::mutableAttribute(): index" << id << "out of range for" << _attributes.size() << "attributes", nullptr);
CORRADE_ASSERT(Implementation::vertexFormatFor<T>() == _attributes[id].format,
"Trade::MeshData::mutableAttribute(): improper type requested for" << _attributes[id].name << "of format" << _attributes[id].format, nullptr);
return Containers::arrayCast<T>(reinterpret_cast<Containers::StridedArrayView1D<char>&>(_attributes[id].data));
}
template<class T> Containers::StridedArrayView1D<const T> MeshData::attribute(MeshAttribute name, UnsignedInt id) const {
const UnsignedInt attributeId = attributeFor(name, id);
CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::attribute(): index" << id << "out of range for" << attributeCount(name) << name << "attributes", {});
return attribute<T>(attributeId);
}
template<class T> Containers::StridedArrayView1D<T> MeshData::mutableAttribute(MeshAttribute name, UnsignedInt id) {
CORRADE_ASSERT(_vertexDataFlags & DataFlag::Mutable,
"Trade::MeshData::mutableAttribute(): vertex data not mutable", {});
const UnsignedInt attributeId = attributeFor(name, id);
CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::mutableAttribute(): index" << id << "out of range for" << attributeCount(name) << name << "attributes", {});
return mutableAttribute<T>(attributeId);
}
}}
#endif

403
src/Magnum/Trade/Test/MeshDataTest.cpp

@ -51,6 +51,7 @@ struct MeshDataTest: TestSuite::Tester {
void constructAttributeWrongFormat();
void constructAttributeTypeErased();
void constructAttributeTypeErasedWrongStride();
void constructAttributeNonOwningArray();
void construct();
void constructIndexless();
@ -59,6 +60,12 @@ struct MeshDataTest: TestSuite::Tester {
void constructIndexlessAttributeless();
void constructIndexlessAttributelessZeroVertices();
void constructNotOwned();
void constructIndicesNotOwned();
void constructVerticesNotOwned();
void constructIndexlessNotOwned();
void constructAttributelessNotOwned();
void constructIndexDataButNotIndexed();
void constructVertexDataButNoAttributes();
void constructVertexDataButNoVertices();
@ -66,6 +73,12 @@ struct MeshDataTest: TestSuite::Tester {
void constructIndicesNotContained();
void constructAttributeNotContained();
void constructInconsitentVertexCount();
void constructNotOwnedIndexFlagOwned();
void constructNotOwnedVertexFlagOwned();
void constructIndicesNotOwnedFlagOwned();
void constructVerticesNotOwnedFlagOwned();
void constructIndexlessNotOwnedFlagOwned();
void constructAttributelessNotOwnedFlagOwned();
void constructCopy();
void constructMove();
@ -83,6 +96,8 @@ struct MeshDataTest: TestSuite::Tester {
template<class T> void colorsAsArray();
void colorsIntoArrayInvalidSize();
void mutableAccessNotAllowed();
void indicesNotIndexed();
void indicesWrongType();
@ -93,6 +108,24 @@ struct MeshDataTest: TestSuite::Tester {
void releaseVertexData();
};
struct {
const char* name;
DataFlags indexDataFlags, vertexDataFlags;
} NotOwnedData[] {
{"", {}, {}},
{"indices mutable", DataFlag::Mutable, {}},
{"vertices mutable", {}, DataFlag::Mutable},
{"both mutable", DataFlag::Mutable, DataFlag::Mutable}
};
struct {
const char* name;
DataFlags dataFlags;
} SingleNotOwnedData[] {
{"", {}},
{"mutable", DataFlag::Mutable}
};
MeshDataTest::MeshDataTest() {
addTests({&MeshDataTest::customAttributeName,
&MeshDataTest::customAttributeNameTooLarge,
@ -109,21 +142,36 @@ MeshDataTest::MeshDataTest() {
&MeshDataTest::constructAttributeWrongFormat,
&MeshDataTest::constructAttributeTypeErased,
&MeshDataTest::constructAttributeTypeErasedWrongStride,
&MeshDataTest::constructAttributeNonOwningArray,
&MeshDataTest::construct,
&MeshDataTest::constructIndexless,
&MeshDataTest::constructIndexlessZeroVertices,
&MeshDataTest::constructAttributeless,
&MeshDataTest::constructIndexlessAttributeless,
&MeshDataTest::constructIndexlessAttributelessZeroVertices,
&MeshDataTest::constructIndexlessAttributelessZeroVertices});
&MeshDataTest::constructIndexDataButNotIndexed,
addInstancedTests({&MeshDataTest::constructNotOwned},
Containers::arraySize(NotOwnedData));
addInstancedTests({&MeshDataTest::constructIndicesNotOwned,
&MeshDataTest::constructVerticesNotOwned,
&MeshDataTest::constructIndexlessNotOwned,
&MeshDataTest::constructAttributelessNotOwned},
Containers::arraySize(SingleNotOwnedData));
addTests({&MeshDataTest::constructIndexDataButNotIndexed,
&MeshDataTest::constructVertexDataButNoAttributes,
&MeshDataTest::constructVertexDataButNoVertices,
&MeshDataTest::constructAttributelessInvalidIndices,
&MeshDataTest::constructIndicesNotContained,
&MeshDataTest::constructAttributeNotContained,
&MeshDataTest::constructInconsitentVertexCount,
&MeshDataTest::constructNotOwnedIndexFlagOwned,
&MeshDataTest::constructNotOwnedVertexFlagOwned,
&MeshDataTest::constructIndicesNotOwnedFlagOwned,
&MeshDataTest::constructVerticesNotOwnedFlagOwned,
&MeshDataTest::constructIndexlessNotOwnedFlagOwned,
&MeshDataTest::constructAttributelessNotOwnedFlagOwned,
&MeshDataTest::constructCopy,
&MeshDataTest::constructMove,
@ -146,6 +194,8 @@ MeshDataTest::MeshDataTest() {
&MeshDataTest::colorsAsArray<Color4>,
&MeshDataTest::colorsIntoArrayInvalidSize,
&MeshDataTest::mutableAccessNotAllowed,
&MeshDataTest::indicesNotIndexed,
&MeshDataTest::indicesWrongType,
@ -312,6 +362,13 @@ void MeshDataTest::constructAttributeTypeErasedWrongStride() {
CORRADE_COMPARE(out.str(), "Trade::MeshAttributeData: view stride 1 is not large enough to contain VertexFormat::Vector3\n");
}
void MeshDataTest::constructAttributeNonOwningArray() {
const MeshAttributeData data[3];
Containers::Array<MeshAttributeData> array = meshAttributeDataNonOwningArray(data);
CORRADE_COMPARE(array.size(), 3);
CORRADE_COMPARE(static_cast<const void*>(array.data()), data);
}
void MeshDataTest::construct() {
struct Vertex {
Vector3 position;
@ -360,9 +417,13 @@ void MeshDataTest::construct() {
std::move(vertexData), {positions, textureCoordinates, normals, textureCoordinates, ids}, &importerState};
/* Basics */
CORRADE_COMPARE(data.indexDataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_COMPARE(data.vertexDataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_COMPARE(data.primitive(), MeshPrimitive::Triangles);
CORRADE_COMPARE(static_cast<const void*>(data.indexData()), indexView.data());
CORRADE_COMPARE(static_cast<const void*>(data.vertexData()), vertexView.data());
CORRADE_COMPARE(static_cast<void*>(data.mutableIndexData()), indexView.data());
CORRADE_COMPARE(static_cast<void*>(data.mutableVertexData()), vertexView.data());
CORRADE_COMPARE(data.importerState(), &importerState);
/* Index access */
@ -400,6 +461,11 @@ void MeshDataTest::construct() {
CORRADE_COMPARE(data.attribute<Vector3>(2)[2], Vector3::zAxis());
CORRADE_COMPARE(data.attribute<Vector2>(3)[1], (Vector2{0.250f, 0.375f}));
CORRADE_COMPARE(data.attribute<Short>(4)[1], -374);
CORRADE_COMPARE(data.mutableAttribute<Vector3>(0)[1], (Vector3{0.4f, 0.5f, 0.6f}));
CORRADE_COMPARE(data.mutableAttribute<Vector2>(1)[0], (Vector2{0.000f, 0.125f}));
CORRADE_COMPARE(data.mutableAttribute<Vector3>(2)[2], Vector3::zAxis());
CORRADE_COMPARE(data.mutableAttribute<Vector2>(3)[1], (Vector2{0.250f, 0.375f}));
CORRADE_COMPARE(data.mutableAttribute<Short>(4)[1], -374);
/* Attribute access by name */
CORRADE_VERIFY(data.hasAttribute(MeshAttribute::Position));
@ -438,6 +504,11 @@ void MeshDataTest::construct() {
CORRADE_COMPARE(data.attribute<Vector2>(MeshAttribute::TextureCoordinates, 0)[0], (Vector2{0.000f, 0.125f}));
CORRADE_COMPARE(data.attribute<Vector2>(MeshAttribute::TextureCoordinates, 1)[1], (Vector2{0.250f, 0.375f}));
CORRADE_COMPARE(data.attribute<Short>(meshAttributeCustom(13))[2], 22);
CORRADE_COMPARE(data.mutableAttribute<Vector3>(MeshAttribute::Position)[1], (Vector3{0.4f, 0.5f, 0.6f}));
CORRADE_COMPARE(data.mutableAttribute<Vector3>(MeshAttribute::Normal)[2], Vector3::zAxis());
CORRADE_COMPARE(data.mutableAttribute<Vector2>(MeshAttribute::TextureCoordinates, 0)[0], (Vector2{0.000f, 0.125f}));
CORRADE_COMPARE(data.mutableAttribute<Vector2>(MeshAttribute::TextureCoordinates, 1)[1], (Vector2{0.250f, 0.375f}));
CORRADE_COMPARE(data.attribute<Short>(meshAttributeCustom(13))[2], 22);
}
void MeshDataTest::constructIndexless() {
@ -450,6 +521,10 @@ void MeshDataTest::constructIndexless() {
int importerState;
MeshAttributeData positions{MeshAttribute::Position, vertexView};
MeshData data{MeshPrimitive::LineLoop, std::move(vertexData), {positions}, &importerState};
CORRADE_COMPARE(data.indexDataFlags(), DataFlag::Owned|DataFlag::Mutable);
/* These are empty so it doesn't matter, but this is a nice non-restrictive
default */
CORRADE_COMPARE(data.vertexDataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_COMPARE(data.primitive(), MeshPrimitive::LineLoop);
CORRADE_COMPARE(data.indexData(), nullptr);
CORRADE_COMPARE(data.importerState(), &importerState);
@ -487,6 +562,10 @@ void MeshDataTest::constructAttributeless() {
int importerState;
MeshIndexData indices{indexView};
MeshData data{MeshPrimitive::TriangleStrip, std::move(indexData), indices, &importerState};
/* These are empty so it doesn't matter, but this is a nice non-restrictive
default */
CORRADE_COMPARE(data.indexDataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_COMPARE(data.vertexDataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_COMPARE(data.primitive(), MeshPrimitive::TriangleStrip);
CORRADE_COMPARE(data.vertexData(), nullptr);
CORRADE_COMPARE(data.importerState(), &importerState);
@ -502,9 +581,215 @@ void MeshDataTest::constructAttributeless() {
CORRADE_COMPARE(data.attributeCount(), 0);
}
void MeshDataTest::constructNotOwned() {
auto&& instanceData = NotOwnedData[testCaseInstanceId()];
setTestCaseDescription(instanceData.name);
UnsignedShort indexData[]{0, 1, 0};
Vector2 vertexData[]{{0.1f, 0.2f}, {0.4f, 0.5f}};
int importerState;
MeshIndexData indices{indexData};
MeshAttributeData positions{MeshAttribute::Position, Containers::arrayView(vertexData)};
MeshData data{MeshPrimitive::Triangles, instanceData.indexDataFlags, Containers::arrayView(indexData), indices, instanceData.vertexDataFlags, Containers::arrayView(vertexData), {positions}, &importerState};
CORRADE_COMPARE(data.indexDataFlags(), instanceData.indexDataFlags);
CORRADE_COMPARE(data.vertexDataFlags(), instanceData.vertexDataFlags);
CORRADE_COMPARE(data.primitive(), MeshPrimitive::Triangles);
CORRADE_COMPARE(static_cast<const void*>(data.indexData()), +indexData);
CORRADE_COMPARE(static_cast<const void*>(data.vertexData()), +vertexData);
if(instanceData.indexDataFlags & DataFlag::Mutable)
CORRADE_COMPARE(static_cast<const void*>(data.mutableIndexData()), +indexData);
if(instanceData.vertexDataFlags & DataFlag::Mutable)
CORRADE_COMPARE(static_cast<const void*>(data.mutableVertexData()), +vertexData);
CORRADE_COMPARE(data.importerState(), &importerState);
CORRADE_VERIFY(data.isIndexed());
CORRADE_COMPARE(data.indexCount(), 3);
CORRADE_COMPARE(data.indexType(), MeshIndexType::UnsignedShort);
CORRADE_COMPARE(data.indices<UnsignedShort>()[1], 1);
CORRADE_COMPARE(data.indices<UnsignedShort>()[2], 0);
if(instanceData.indexDataFlags & DataFlag::Mutable) {
CORRADE_COMPARE(data.mutableIndices<UnsignedShort>()[1], 1);
CORRADE_COMPARE(data.mutableIndices<UnsignedShort>()[2], 0);
}
CORRADE_COMPARE(data.vertexCount(), 2);
CORRADE_COMPARE(data.attributeCount(), 1);
CORRADE_COMPARE(data.attributeName(0), MeshAttribute::Position);
CORRADE_COMPARE(data.attributeFormat(0), VertexFormat::Vector2);
CORRADE_COMPARE(data.attributeOffset(0), 0);
CORRADE_COMPARE(data.attributeStride(0), sizeof(Vector2));
CORRADE_COMPARE(data.attribute<Vector2>(0)[0], (Vector2{0.1f, 0.2f}));
CORRADE_COMPARE(data.attribute<Vector2>(0)[1], (Vector2{0.4f, 0.5f}));
if(instanceData.vertexDataFlags & DataFlag::Mutable) {
CORRADE_COMPARE(data.mutableAttribute<Vector2>(0)[0], (Vector2{0.1f, 0.2f}));
CORRADE_COMPARE(data.mutableAttribute<Vector2>(0)[1], (Vector2{0.4f, 0.5f}));
}
}
void MeshDataTest::constructIndicesNotOwned() {
auto&& instanceData = SingleNotOwnedData[testCaseInstanceId()];
setTestCaseDescription(instanceData.name);
UnsignedShort indexData[]{0, 1, 0};
Containers::Array<char> vertexData{2*sizeof(Vector2)};
auto vertexView = Containers::arrayCast<Vector2>(vertexData);
vertexView[0] = {0.1f, 0.2f};
vertexView[1] = {0.4f, 0.5f};
int importerState;
MeshIndexData indices{indexData};
MeshAttributeData positions{MeshAttribute::Position, vertexView};
MeshData data{MeshPrimitive::Triangles, instanceData.dataFlags, Containers::arrayView(indexData), indices, std::move(vertexData), {positions}, &importerState};
CORRADE_COMPARE(data.indexDataFlags(), instanceData.dataFlags);
CORRADE_COMPARE(data.vertexDataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_COMPARE(data.primitive(), MeshPrimitive::Triangles);
CORRADE_COMPARE(static_cast<const void*>(data.indexData()), +indexData);
CORRADE_COMPARE(static_cast<const void*>(data.vertexData()), vertexView.data());
if(instanceData.dataFlags & DataFlag::Mutable)
CORRADE_COMPARE(static_cast<const void*>(data.mutableIndexData()), +indexData);
CORRADE_COMPARE(static_cast<const void*>(data.mutableVertexData()), vertexView.data());
CORRADE_COMPARE(data.importerState(), &importerState);
CORRADE_VERIFY(data.isIndexed());
CORRADE_COMPARE(data.indexCount(), 3);
CORRADE_COMPARE(data.indexType(), MeshIndexType::UnsignedShort);
CORRADE_COMPARE(data.indices<UnsignedShort>()[1], 1);
CORRADE_COMPARE(data.indices<UnsignedShort>()[2], 0);
if(instanceData.dataFlags & DataFlag::Mutable) {
CORRADE_COMPARE(data.mutableIndices<UnsignedShort>()[1], 1);
CORRADE_COMPARE(data.mutableIndices<UnsignedShort>()[2], 0);
}
CORRADE_COMPARE(data.vertexCount(), 2);
CORRADE_COMPARE(data.attributeCount(), 1);
CORRADE_COMPARE(data.attributeName(0), MeshAttribute::Position);
CORRADE_COMPARE(data.attributeFormat(0), VertexFormat::Vector2);
CORRADE_COMPARE(data.attributeOffset(0), 0);
CORRADE_COMPARE(data.attributeStride(0), sizeof(Vector2));
CORRADE_COMPARE(data.attribute<Vector2>(0)[0], (Vector2{0.1f, 0.2f}));
CORRADE_COMPARE(data.attribute<Vector2>(0)[1], (Vector2{0.4f, 0.5f}));
CORRADE_COMPARE(data.mutableAttribute<Vector2>(0)[0], (Vector2{0.1f, 0.2f}));
CORRADE_COMPARE(data.mutableAttribute<Vector2>(0)[1], (Vector2{0.4f, 0.5f}));
}
void MeshDataTest::constructVerticesNotOwned() {
auto&& instanceData = SingleNotOwnedData[testCaseInstanceId()];
setTestCaseDescription(instanceData.name);
Containers::Array<char> indexData{3*sizeof(UnsignedShort)};
auto indexView = Containers::arrayCast<UnsignedShort>(indexData);
indexView[0] = 0;
indexView[1] = 1;
indexView[2] = 0;
Vector2 vertexData[]{{0.1f, 0.2f}, {0.4f, 0.5f}};
int importerState;
MeshIndexData indices{indexView};
MeshAttributeData positions{MeshAttribute::Position, Containers::arrayView(vertexData)};
MeshData data{MeshPrimitive::Triangles, std::move(indexData), indices, instanceData.dataFlags, Containers::arrayView(vertexData), {positions}, &importerState};
CORRADE_COMPARE(data.indexDataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_COMPARE(data.vertexDataFlags(), instanceData.dataFlags);
CORRADE_COMPARE(data.primitive(), MeshPrimitive::Triangles);
CORRADE_COMPARE(static_cast<const void*>(data.indexData()), indexView.data());
CORRADE_COMPARE(static_cast<const void*>(data.vertexData()), +vertexData);
CORRADE_COMPARE(static_cast<const void*>(data.mutableIndexData()), indexView.data());
if(instanceData.dataFlags & DataFlag::Mutable)
CORRADE_COMPARE(static_cast<const void*>(data.mutableVertexData()), +vertexData);
CORRADE_COMPARE(data.importerState(), &importerState);
CORRADE_VERIFY(data.isIndexed());
CORRADE_COMPARE(data.indexCount(), 3);
CORRADE_COMPARE(data.indexType(), MeshIndexType::UnsignedShort);
CORRADE_COMPARE(data.indices<UnsignedShort>()[1], 1);
CORRADE_COMPARE(data.indices<UnsignedShort>()[2], 0);
CORRADE_COMPARE(data.mutableIndices<UnsignedShort>()[1], 1);
CORRADE_COMPARE(data.mutableIndices<UnsignedShort>()[2], 0);
CORRADE_COMPARE(data.vertexCount(), 2);
CORRADE_COMPARE(data.attributeCount(), 1);
CORRADE_COMPARE(data.attributeName(0), MeshAttribute::Position);
CORRADE_COMPARE(data.attributeFormat(0), VertexFormat::Vector2);
CORRADE_COMPARE(data.attributeOffset(0), 0);
CORRADE_COMPARE(data.attributeStride(0), sizeof(Vector2));
CORRADE_COMPARE(data.attribute<Vector2>(0)[0], (Vector2{0.1f, 0.2f}));
CORRADE_COMPARE(data.attribute<Vector2>(0)[1], (Vector2{0.4f, 0.5f}));
if(instanceData.dataFlags & DataFlag::Mutable) {
CORRADE_COMPARE(data.mutableAttribute<Vector2>(0)[0], (Vector2{0.1f, 0.2f}));
CORRADE_COMPARE(data.mutableAttribute<Vector2>(0)[1], (Vector2{0.4f, 0.5f}));
}
}
void MeshDataTest::constructIndexlessNotOwned() {
auto&& instanceData = SingleNotOwnedData[testCaseInstanceId()];
setTestCaseDescription(instanceData.name);
Vector2 vertexData[]{{0.1f, 0.2f}, {0.4f, 0.5f}};
int importerState;
MeshAttributeData positions{MeshAttribute::Position, Containers::arrayView(vertexData)};
MeshData data{MeshPrimitive::LineLoop, instanceData.dataFlags, vertexData, {positions}, &importerState};
CORRADE_COMPARE(data.indexDataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_COMPARE(data.vertexDataFlags(), instanceData.dataFlags);
CORRADE_COMPARE(data.primitive(), MeshPrimitive::LineLoop);
CORRADE_COMPARE(data.indexData(), nullptr);
if(instanceData.dataFlags & DataFlag::Mutable)
CORRADE_COMPARE(data.mutableIndexData(), nullptr);
CORRADE_COMPARE(data.importerState(), &importerState);
CORRADE_VERIFY(!data.isIndexed());
CORRADE_COMPARE(data.vertexCount(), 2);
CORRADE_COMPARE(data.attributeCount(), 1);
CORRADE_COMPARE(data.attributeFormat(MeshAttribute::Position), VertexFormat::Vector2);
CORRADE_COMPARE(data.attribute<Vector2>(MeshAttribute::Position)[1], (Vector2{0.4f, 0.5f}));
if(instanceData.dataFlags & DataFlag::Mutable)
CORRADE_COMPARE(data.mutableAttribute<Vector2>(MeshAttribute::Position)[1], (Vector2{0.4f, 0.5f}));
}
void MeshDataTest::constructAttributelessNotOwned() {
auto&& instanceData = SingleNotOwnedData[testCaseInstanceId()];
setTestCaseDescription(instanceData.name);
UnsignedShort indexData[]{0, 1, 0};
int importerState;
MeshIndexData indices{indexData};
MeshData data{MeshPrimitive::TriangleStrip, instanceData.dataFlags, indexData, indices, &importerState};
CORRADE_COMPARE(data.indexDataFlags(), instanceData.dataFlags);
CORRADE_COMPARE(data.vertexDataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_COMPARE(data.primitive(), MeshPrimitive::TriangleStrip);
CORRADE_COMPARE(data.vertexData(), nullptr);
if(instanceData.dataFlags & DataFlag::Mutable)
CORRADE_COMPARE(data.mutableVertexData(), nullptr);
CORRADE_COMPARE(data.importerState(), &importerState);
CORRADE_VERIFY(data.isIndexed());
CORRADE_COMPARE(data.indexCount(), 3);
CORRADE_COMPARE(data.indexType(), MeshIndexType::UnsignedShort);
CORRADE_COMPARE(data.indices<UnsignedShort>()[0], 0);
CORRADE_COMPARE(data.indices<UnsignedShort>()[1], 1);
CORRADE_COMPARE(data.indices<UnsignedShort>()[2], 0);
if(instanceData.dataFlags & DataFlag::Mutable) {
CORRADE_COMPARE(data.mutableIndices<UnsignedShort>()[0], 0);
CORRADE_COMPARE(data.mutableIndices<UnsignedShort>()[1], 1);
CORRADE_COMPARE(data.mutableIndices<UnsignedShort>()[2], 0);
}
CORRADE_COMPARE(data.vertexCount(), 0); /** @todo what to return here? */
CORRADE_COMPARE(data.attributeCount(), 0);
}
void MeshDataTest::constructIndexlessAttributeless() {
int importerState;
MeshData data{MeshPrimitive::TriangleStrip, 37, &importerState};
/* These are both empty so it doesn't matter, but this is a nice
non-restrictive default */
CORRADE_COMPARE(data.indexDataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_COMPARE(data.vertexDataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_COMPARE(data.primitive(), MeshPrimitive::TriangleStrip);
CORRADE_COMPARE(data.indexData(), nullptr);
CORRADE_COMPARE(data.vertexData(), nullptr);
@ -606,6 +891,91 @@ void MeshDataTest::constructInconsitentVertexCount() {
"Trade::MeshData: attribute 1 has 2 vertices but 3 expected\n");
}
void MeshDataTest::constructNotOwnedIndexFlagOwned() {
const UnsignedShort indexData[]{0, 1, 0};
const Vector2 vertexData[]{{0.1f, 0.2f}, {0.4f, 0.5f}};
MeshIndexData indices{indexData};
MeshAttributeData positions{MeshAttribute::Position, Containers::arrayView(vertexData)};
std::ostringstream out;
Error redirectError{&out};
MeshData data{MeshPrimitive::Triangles, DataFlag::Owned, indexData, indices, {}, vertexData, {positions}};
CORRADE_COMPARE(out.str(),
"Trade::MeshData: can't construct with non-owned index data but Trade::DataFlag::Owned\n");
}
void MeshDataTest::constructNotOwnedVertexFlagOwned() {
const UnsignedShort indexData[]{0, 1, 0};
const Vector2 vertexData[]{{0.1f, 0.2f}, {0.4f, 0.5f}};
MeshIndexData indices{indexData};
MeshAttributeData positions{MeshAttribute::Position, Containers::arrayView(vertexData)};
std::ostringstream out;
Error redirectError{&out};
MeshData data{MeshPrimitive::Triangles, {}, indexData, indices, DataFlag::Owned, vertexData, {positions}};
CORRADE_COMPARE(out.str(),
"Trade::MeshData: can't construct with non-owned vertex data but Trade::DataFlag::Owned\n");
}
void MeshDataTest::constructIndicesNotOwnedFlagOwned() {
UnsignedShort indexData[]{0, 1, 0};
Containers::Array<char> vertexData{2*sizeof(Vector2)};
auto vertexView = Containers::arrayCast<Vector2>(vertexData);
vertexView[0] = {0.1f, 0.2f};
vertexView[1] = {0.4f, 0.5f};
MeshIndexData indices{indexData};
MeshAttributeData positions{MeshAttribute::Position, vertexView};
std::ostringstream out;
Error redirectError{&out};
MeshData data{MeshPrimitive::Triangles, DataFlag::Owned, indexData, indices, std::move(vertexData), {positions}};
CORRADE_COMPARE(out.str(),
"Trade::MeshData: can't construct with non-owned index data but Trade::DataFlag::Owned\n");
}
void MeshDataTest::constructVerticesNotOwnedFlagOwned() {
Containers::Array<char> indexData{3*sizeof(UnsignedShort)};
auto indexView = Containers::arrayCast<UnsignedShort>(indexData);
indexView[0] = 0;
indexView[1] = 1;
indexView[2] = 0;
Vector2 vertexData[]{{0.1f, 0.2f}, {0.4f, 0.5f}};
MeshIndexData indices{indexView};
MeshAttributeData positions{MeshAttribute::Position, Containers::arrayView(vertexData)};
std::ostringstream out;
Error redirectError{&out};
MeshData data{MeshPrimitive::Triangles, std::move(indexData), indices, DataFlag::Owned, vertexData, {positions}};
CORRADE_COMPARE(out.str(),
"Trade::MeshData: can't construct with non-owned vertex data but Trade::DataFlag::Owned\n");
}
void MeshDataTest::constructIndexlessNotOwnedFlagOwned() {
const Vector2 vertexData[]{{0.1f, 0.2f}, {0.4f, 0.5f}};
MeshAttributeData positions{MeshAttribute::Position, Containers::arrayView(vertexData)};
std::ostringstream out;
Error redirectError{&out};
MeshData data{MeshPrimitive::Triangles, DataFlag::Owned, vertexData, {positions}};
CORRADE_COMPARE(out.str(),
"Trade::MeshData: can't construct with non-owned vertex data but Trade::DataFlag::Owned\n");
}
void MeshDataTest::constructAttributelessNotOwnedFlagOwned() {
const UnsignedShort indexData[]{0, 1, 0};
MeshIndexData indices{indexData};
std::ostringstream out;
Error redirectError{&out};
MeshData data{MeshPrimitive::Triangles, DataFlag::Owned, indexData, indices};
CORRADE_COMPARE(out.str(),
"Trade::MeshData: can't construct with non-owned index data but Trade::DataFlag::Owned\n");
}
void MeshDataTest::constructCopy() {
CORRADE_VERIFY(!(std::is_constructible<MeshData, const MeshData&>{}));
CORRADE_VERIFY(!(std::is_assignable<MeshData, const MeshData&>{}));
@ -630,6 +1000,8 @@ void MeshDataTest::constructMove() {
MeshData b{std::move(a)};
CORRADE_COMPARE(b.indexDataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_COMPARE(b.vertexDataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_COMPARE(b.primitive(), MeshPrimitive::Triangles);
CORRADE_COMPARE(static_cast<const void*>(b.indexData()), indexView.data());
CORRADE_COMPARE(static_cast<const void*>(b.vertexData()), vertexView.data());
@ -653,6 +1025,8 @@ void MeshDataTest::constructMove() {
MeshData c{MeshPrimitive::LineLoop, 37};
c = std::move(b);
CORRADE_COMPARE(c.indexDataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_COMPARE(c.vertexDataFlags(), DataFlag::Owned|DataFlag::Mutable);
CORRADE_COMPARE(c.primitive(), MeshPrimitive::Triangles);
CORRADE_COMPARE(static_cast<const void*>(c.indexData()), indexView.data());
CORRADE_COMPARE(static_cast<const void*>(c.vertexData()), vertexView.data());
@ -851,6 +1225,31 @@ void MeshDataTest::colorsIntoArrayInvalidSize() {
"Trade::MeshData::colorsInto(): expected a view with 3 elements but got 2\n");
}
void MeshDataTest::mutableAccessNotAllowed() {
const UnsignedShort indexData[]{0, 1, 0};
const Vector2 vertexData[]{{0.1f, 0.2f}, {0.4f, 0.5f}};
MeshIndexData indices{indexData};
MeshAttributeData positions{MeshAttribute::Position, Containers::arrayView(vertexData)};
MeshData data{MeshPrimitive::Triangles, {}, indexData, indices, {}, vertexData, {positions}};
CORRADE_COMPARE(data.indexDataFlags(), DataFlags{});
CORRADE_COMPARE(data.vertexDataFlags(), DataFlags{});
std::ostringstream out;
Error redirectError{&out};
data.mutableIndexData();
data.mutableVertexData();
data.mutableIndices<UnsignedShort>();
data.mutableAttribute<Vector2>(0);
data.mutableAttribute<Vector2>(MeshAttribute::Position);
CORRADE_COMPARE(out.str(),
"Trade::MeshData::mutableIndexData(): index data not mutable\n"
"Trade::MeshData::mutableVertexData(): vertex data not mutable\n"
"Trade::MeshData::mutableIndices(): index data not mutable\n"
"Trade::MeshData::mutableAttribute(): vertex data not mutable\n"
"Trade::MeshData::mutableAttribute(): vertex data not mutable\n");
}
void MeshDataTest::indicesNotIndexed() {
MeshData data{MeshPrimitive::Triangles, 37};

Loading…
Cancel
Save