Browse Source

Trade: support strided index buffers in MeshData.

Also not something the classic GPU vertex pipeline can handle, but
useful for other scenarios. Subsequently a support for array indices
will be added, allowing to directly represent for example OBJ files,
where each attribute has its own index buffer.
pull/547/head
Vladimír Vondruš 4 years ago
parent
commit
2d9cedfdca
  1. 21
      doc/changelog.dox
  2. 6
      doc/snippets/MagnumTrade.cpp
  3. 73
      src/Magnum/Trade/MeshData.cpp
  4. 165
      src/Magnum/Trade/MeshData.h
  5. 342
      src/Magnum/Trade/Test/MeshDataTest.cpp

21
doc/changelog.dox

@ -452,9 +452,10 @@ See also:
@ref Trade::PhongMaterialData::commonTextureCoordinates() exposing a
common texture coordinate set as a complement to a per-texture property
added in 2020.06
- @ref Trade::MeshData now allows zero and negative strides for better
data layout flexibility. This is however not commonly supported by GPU APIs
and tools like @ref MeshTools::compile() assert in that case.
- @ref Trade::MeshData now allows strided index buffers and zero and negative
attribute strides for better data layout flexibility. This is however not
commonly supported by GPU APIs and tools like @ref MeshTools::compile()
assert in that case.
- Added @ref Trade::MeshData::findAttributeId() for an ability to check that
an attribute exists and retrieve its ID in a single step, avoiding a double
lookup compared to @relativeref{Trade::MeshData,hasAttribute()} +
@ -878,10 +879,16 @@ See also:
@cpp Trade::AbstractMaterialData @ce aliases to, doesn't have a
@cpp virtual @ce destructor as subclasses with extra data members aren't a
desired use case anymore.
- @ref Trade::MeshData now allows zero and negative strides for better
data layout flexibility, however as this is not commonly supported by GPU
APIs, it implies the user is now expected to validate this value when
passing it there.
- @ref Trade::MeshData now allows strided index buffers and zero and negative
attribute strides for better data layout flexibility, however as this is
not commonly supported by GPU APIs, it implies the user is now expected to
validate the data layout when passing it there. Due to this change, the
@ref Trade::MeshData::indices() and
@relativeref{Trade::MeshData,mutableIndices()} accessors now return a
@relativeref{Corrade,Containers::StridedArrayView} instead of an
@relativeref{Corrade,Containers::ArrayView} --- either change your code to
accept a strided view instead or use @relativeref{Corrade,Containers::StridedArrayView::asContiguous()} to get a
contiguous @relativeref{Corrade,Containers::ArrayView} again.
- @ref Trade::SceneData constructor taking a @ref std::vector of 2D and 3D
children that got deprecated in favor of
@ref Trade::SceneData::SceneData(SceneMappingType, UnsignedLong, Containers::Array<char>&&, Containers::Array<SceneFieldData>&&, const void*)

6
doc/snippets/MagnumTrade.cpp

@ -736,10 +736,12 @@ MeshTools::transformPointsInPlace(Matrix4::scaling(Vector3{2.0f}),
Trade::MeshData data{MeshPrimitive::Points, 0};
/* [MeshData-usage-special-layouts] */
if(data.attributeStride(Trade::MeshAttribute::Position) <= 0 ||
data.attributeStride(Trade::MeshAttribute::Normal) <= 0)
data.attributeStride(Trade::MeshAttribute::Normal) <= 0 ||
(data.isIndexed() && !data.indices().isContiguous()))
Fatal{} << "Uh oh";
// Now it's safe to use the Position and Normal attributes in a GPU mesh
// Now it's safe to use the Position and Normal attributes and the index buffer
// in a GPU mesh
/* [MeshData-usage-special-layouts] */
}

73
src/Magnum/Trade/MeshData.cpp

@ -37,12 +37,12 @@
namespace Magnum { namespace Trade {
MeshIndexData::MeshIndexData(const MeshIndexType type, const Containers::ArrayView<const void> data) noexcept: _type{type}, _data{data} {
CORRADE_ASSERT(data.size()%meshIndexTypeSize(type) == 0,
"Trade::MeshIndexData: view size" << data.size() << "does not correspond to" << type, );
}
MeshIndexData::MeshIndexData(const Containers::StridedArrayView2D<const char>& data) noexcept {
MeshIndexData::MeshIndexData(const Containers::StridedArrayView2D<const char>& data) noexcept:
/* Delegating to the constexpr function in order to reuse the stride size
assert. The index type is not checked there so we can set it to nothing
and then overwrite below. */
MeshIndexData{MeshIndexType{}, {{nullptr, ~std::size_t{}}, data.data(), data.size()[0], data.stride()[0]}}
{
/* Second dimension being zero indicates a non-indexed mesh */
if(data.size()[1] == 0) {
_type = MeshIndexType{};
@ -54,8 +54,8 @@ MeshIndexData::MeshIndexData(const Containers::StridedArrayView2D<const char>& d
else if(data.size()[1] == 1) _type = MeshIndexType::UnsignedByte;
else CORRADE_ASSERT_UNREACHABLE("Trade::MeshIndexData: expected index type size 1, 2 or 4 but got" << data.size()[1], );
CORRADE_ASSERT(data.isContiguous(), "Trade::MeshIndexData: view is not contiguous", );
_data = data.asContiguous();
CORRADE_ASSERT(data.isContiguous<1>(),
"Trade::MeshIndexData: second view dimension is not contiguous", );
}
MeshAttributeData::MeshAttributeData(const MeshAttribute name, const VertexFormat format, const Containers::StridedArrayView1D<const void>& data, UnsignedShort arraySize) noexcept: MeshAttributeData{nullptr, name, format, data, arraySize} {
@ -83,10 +83,22 @@ Containers::Array<MeshAttributeData> meshAttributeDataNonOwningArray(const Conta
return Containers::Array<MeshAttributeData>{const_cast<MeshAttributeData*>(view.data()), view.size(), reinterpret_cast<void(*)(MeshAttributeData*, std::size_t)>(Implementation::nonOwnedArrayDeleter)};
}
MeshData::MeshData(const MeshPrimitive primitive, Containers::Array<char>&& indexData, const MeshIndexData& indices, Containers::Array<char>&& vertexData, Containers::Array<MeshAttributeData>&& attributes, const UnsignedInt vertexCount, const void* const importerState) noexcept: _primitive{primitive}, _indexType{indices._type}, _indexDataFlags{DataFlag::Owned|DataFlag::Mutable}, _vertexDataFlags{DataFlag::Owned|DataFlag::Mutable}, _importerState{importerState}, _indices{static_cast<const char*>(indices._data.data())}, _attributes{std::move(attributes)}, _indexData{std::move(indexData)}, _vertexData{std::move(vertexData)} {
MeshData::MeshData(const MeshPrimitive primitive, Containers::Array<char>&& indexData, const MeshIndexData& indices, Containers::Array<char>&& vertexData, Containers::Array<MeshAttributeData>&& attributes, const UnsignedInt vertexCount, const void* const importerState) noexcept:
_primitive{primitive}, _indexType{indices._type},
/* Bounds of index stride are checked in MeshIndexData already, so the
cast alone is fine */
_indexStride{Short(indices._data.stride())},
_indexDataFlags{DataFlag::Owned|DataFlag::Mutable},
_vertexDataFlags{DataFlag::Owned|DataFlag::Mutable},
_importerState{importerState},
_indices{static_cast<const char*>(indices._data.data())},
_attributes{std::move(attributes)},
_indexData{std::move(indexData)},
_vertexData{std::move(vertexData)}
{
/* Save index count, only if the indices are actually specified */
if(_indexType != MeshIndexType{})
_indexCount = UnsignedInt(indices._data.size()/meshIndexTypeSize(indices._type));
_indexCount = indices._data.size();
else _indexCount = 0;
/* Save vertex count. If it's passed explicitly, use that (but still check
@ -112,12 +124,23 @@ MeshData::MeshData(const MeshPrimitive primitive, Containers::Array<char>&& inde
#endif
}
#ifndef CORRADE_NO_ASSERT
CORRADE_ASSERT(_indexCount || _indexData.empty(),
"Trade::MeshData: indexData passed for a non-indexed mesh", );
CORRADE_ASSERT(!_indices || (_indices >= _indexData.begin() && _indices + indices._data.size() <= _indexData.end()),
"Trade::MeshData: indices [" << Debug::nospace << static_cast<const void*>(_indices) << Debug::nospace << ":" << Debug::nospace << static_cast<const void*>(_indices + indices._data.size()) << Debug::nospace << "] are not contained in passed indexData array [" << Debug::nospace << static_cast<const void*>(_indexData.begin()) << Debug::nospace << ":" << Debug::nospace << static_cast<const void*>(_indexData.end()) << Debug::nospace << "]", );
if(_indexCount) {
const void* begin = _indices;
/* C integer promotion rules are weird, without the Int the result is
an unsigned 32-bit value that messes things up on 64bit */
const void* end = _indices + Int(_indexCount - 1)*_indexStride;
/* Flip for negative stride */
if(begin > end) std::swap(begin, end);
/* Add the last element size to the higher address */
end = static_cast<const char*>(end) + meshIndexTypeSize(_indexType);
CORRADE_ASSERT(begin >= _indexData.begin() && end <= _indexData.end(),
"Trade::MeshData: indices [" << Debug::nospace << begin << Debug::nospace << ":" << Debug::nospace << end << Debug::nospace << "] are not contained in passed indexData array [" << Debug::nospace << static_cast<const void*>(_indexData.begin()) << Debug::nospace << ":" << Debug::nospace << static_cast<const void*>(_indexData.end()) << Debug::nospace << "]", );
}
#ifndef CORRADE_NO_ASSERT
/* Not checking what's already checked in MeshIndexData / MeshAttributeData
constructors */
for(std::size_t i = 0; i != _attributes.size(); ++i) {
@ -250,13 +273,20 @@ std::size_t MeshData::indexOffset() const {
return _indices - _indexData.data();
}
Short MeshData::indexStride() const {
CORRADE_ASSERT(isIndexed(),
"Trade::MeshData::indexStride(): the mesh is not indexed", {});
return _indexStride;
}
Containers::StridedArrayView2D<const char> MeshData::indices() const {
/* For a non-indexed mesh returning zero size in both dimensions, indexed
mesh with zero indices still has the second dimension non-zero */
if(!isIndexed()) return {};
const std::size_t indexTypeSize = meshIndexTypeSize(_indexType);
/* Build a 2D view using information about attribute type size */
return {{_indices, _indexCount*indexTypeSize}, {_indexCount, indexTypeSize}};
/* Build a 2D view using information about index type size and stride.
We're *sure* the view is correct, so faking the view size */
return {{_indices, ~std::size_t{}}, {_indexCount, indexTypeSize}, {_indexStride, 1}};
}
Containers::StridedArrayView2D<char> MeshData::mutableIndices() {
@ -266,8 +296,9 @@ Containers::StridedArrayView2D<char> MeshData::mutableIndices() {
mesh with zero indices still has the second dimension non-zero */
if(!isIndexed()) return {};
const std::size_t indexTypeSize = meshIndexTypeSize(_indexType);
/* Build a 2D view using information about index type size */
Containers::StridedArrayView2D<const char> out{{_indices, _indexCount*indexTypeSize}, {_indexCount, indexTypeSize}};
/* Build a 2D view using information about index type size and stride.
We're *sure* the view is correct, so faking the view size */
Containers::StridedArrayView2D<const char> out{{_indices, ~std::size_t{}}, {_indexCount, indexTypeSize}, {_indexStride, 1}};
/** @todo some arrayConstCast? UGH */
return Containers::StridedArrayView2D<char>{
/* The view size is there only for a size assert, we're pretty sure the
@ -429,12 +460,14 @@ void MeshData::indicesInto(const Containers::StridedArrayView1D<UnsignedInt>& de
CORRADE_ASSERT(destination.size() == indexCount(), "Trade::MeshData::indicesInto(): expected a view with" << indexCount() << "elements but got" << destination.size(), );
const auto destination1ui = Containers::arrayCast<2, UnsignedInt>(destination);
const Containers::StridedArrayView2D<const char> indexData = indices();
if(_indexType == MeshIndexType::UnsignedInt)
return Utility::copy(Containers::arrayView(reinterpret_cast<const UnsignedInt*>(_indices), _indexCount), destination);
return Utility::copy(Containers::arrayCast<1, const UnsignedInt>(indexData), destination);
else if(_indexType == MeshIndexType::UnsignedShort)
return Math::castInto(Containers::arrayCast<2, const UnsignedShort>(Containers::arrayView(reinterpret_cast<const UnsignedShort*>(_indices), _indexCount)), destination1ui);
return Math::castInto(Containers::arrayCast<2, const UnsignedShort>(indexData), destination1ui);
else if(_indexType == MeshIndexType::UnsignedByte)
return Math::castInto(Containers::arrayCast<2, const UnsignedByte>(Containers::arrayView(reinterpret_cast<const UnsignedByte*>(_indices), _indexCount)), destination1ui);
return Math::castInto(Containers::arrayCast<2, const UnsignedByte>(indexData), destination1ui);
else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}

165
src/Magnum/Trade/MeshData.h

@ -233,54 +233,108 @@ class MAGNUM_TRADE_EXPORT MeshIndexData {
explicit MeshIndexData(std::nullptr_t = nullptr) noexcept: _type{} {}
/**
* @brief Construct with a runtime-specified index type
* @brief Construct a contiguous index array with a runtime-specified index type
* @param type Index type
* @param data Index data
*
* The @p data size is expected to correspond to given @p type (e.g.,
* for @ref MeshIndexType::UnsignedInt the @p data array size should
* be divisible by 4). If you know the @p type at compile time, you can
* use one of the @ref MeshIndexData(Containers::ArrayView<const UnsignedByte>),
* @ref MeshIndexData(Containers::ArrayView<const UnsignedShort>) or
* @ref MeshIndexData(Containers::ArrayView<const UnsignedInt>)
* This overload is picked over @ref MeshIndexData(MeshIndexType, Containers::StridedArrayView1D<const void>)
* if @p data is convertible to a contiguous view. The @p data size is
* then expected to correspond to given @p type (e.g., for
* @ref MeshIndexType::UnsignedInt the @p data array size should be
* divisible by 4). If you know the @p type at compile time, you can
* use one of the @ref MeshIndexData(Containers::StridedArrayView1D<const UnsignedByte>),
* @ref MeshIndexData(Containers::StridedArrayView1D<const UnsignedShort>) or
* @ref MeshIndexData(Containers::StridedArrayView1D<const UnsignedInt>)
* constructors, which infer the index type automatically.
*
* If @p data is empty, the mesh will be treated as indexed but with
* zero indices. To create a non-indexed mesh, use the
* @ref MeshIndexData(std::nullptr_t) constructor.
*/
#ifndef DOXYGEN_GENERATING_OUTPUT
template<class T, class = typename std::enable_if<std::is_convertible<T, Containers::ArrayView<const void>>::value>::type> explicit MeshIndexData(MeshIndexType type, T&& data) noexcept;
#else
explicit MeshIndexData(MeshIndexType type, Containers::ArrayView<const void> data) noexcept;
#endif
/** @brief Construct with unsigned byte indices */
constexpr explicit MeshIndexData(Containers::ArrayView<const UnsignedByte> data) noexcept: _type{MeshIndexType::UnsignedByte}, _data{data} {}
/**
* @brief Construct a strided index array with a runtime-specified index type
* @param type Index type
* @param data Index data
* @m_since_latest
*
* If you know the @p type at compile time, you can use one of the
* @ref MeshIndexData(Containers::StridedArrayView1D<const UnsignedByte>),
* @ref MeshIndexData(Containers::StridedArrayView1D<const UnsignedShort>) or
* @ref MeshIndexData(Containers::StridedArrayView1D<const UnsignedInt>)
* constructors, which infer the index type automatically. The view
* doesn't need to be contiguous and the stride can be even zero or
* negative, but note that such data layout is not commonly supported
* by GPU APIs.
*
* If @p data is empty, the mesh will be treated as indexed but with
* zero indices. To create a non-indexed mesh, use the
* @ref MeshIndexData(std::nullptr_t) constructor.
*/
constexpr explicit MeshIndexData(MeshIndexType type, Containers::StridedArrayView1D<const void> data) noexcept;
/** @brief Construct with unsigned short indices */
constexpr explicit MeshIndexData(Containers::ArrayView<const UnsignedShort> data) noexcept: _type{MeshIndexType::UnsignedShort}, _data{data} {}
/**
* @brief Construct with unsigned byte indices
*
* The view doesn't need to be contiguous and the stride can be even
* zero or negative, but note that such data layout is not commonly
* supported by GPU APIs.
*/
constexpr explicit MeshIndexData(Containers::StridedArrayView1D<const UnsignedByte> data) noexcept: MeshIndexData{MeshIndexType::UnsignedByte, data} {}
/** @brief Construct with unsigned int indices */
constexpr explicit MeshIndexData(Containers::ArrayView<const UnsignedInt> data) noexcept: _type{MeshIndexType::UnsignedInt}, _data{data} {}
/**
* @brief Construct with unsigned short indices
*
* The view doesn't need to be contiguous and the stride can be even
* zero or negative, but note that such data layout is not commonly
* supported by GPU APIs.
*/
constexpr explicit MeshIndexData(Containers::StridedArrayView1D<const UnsignedShort> data) noexcept: MeshIndexData{MeshIndexType::UnsignedShort, data} {}
/**
* @brief Construct with unsigned int indices
*
* The view doesn't need to be contiguous and the stride can be even
* zero or negative, but note that such data layout is not commonly
* supported by GPU APIs.
*/
constexpr explicit MeshIndexData(Containers::StridedArrayView1D<const UnsignedInt> data) noexcept: MeshIndexData{MeshIndexType::UnsignedInt, data} {}
/**
* @brief Constructor
*
* Expects that @p data is contiguous and size of the second dimension
* is either 1, 2 or 4, corresponding to one of the @ref MeshIndexType
* values. As a special case, if second dimension size is 0, the
* constructor is equivalent to @ref MeshIndexData(std::nullptr_t).
* Expects that the second dimension of @p data is contiguous and its
* size is either 1, 2 or 4, corresponding to one of the
* @ref MeshIndexType values. The first dimension doesn't need to be
* contiguous and its stride can be even zero or negative, but note
* that such data layout is not commonly supported by GPU APIs. As a
* special case, if second dimension size is 0, the constructor is
* equivalent to @ref MeshIndexData(std::nullptr_t).
*/
explicit MeshIndexData(const Containers::StridedArrayView2D<const char>& data) noexcept;
/** @brief Index type */
constexpr MeshIndexType type() const { return _type; }
/** @brief Type-erased index data */
constexpr Containers::ArrayView<const void> data() const { return _data; }
/**
* @brief Type-erased index data
*
* In rare cases the stride may be different from the index type size
* and even be zero or negative, such data layouts are however not
* commonly supported by GPU APIs.
*/
constexpr Containers::StridedArrayView1D<const void> data() const { return _data; }
private:
friend MeshData;
MeshIndexType _type;
/* Void so the constructors can be constexpr */
Containers::ArrayView<const void> _data;
Containers::StridedArrayView1D<const void> _data;
};
/**
@ -1111,13 +1165,28 @@ class MAGNUM_TRADE_EXPORT MeshData {
*/
std::size_t indexOffset() const;
/**
* @brief Index stride
* @m_since_latest
*
* Stride between consecutive elements in the @ref indexData() array.
* In rare cases the stride may be different from the index type size
* and even be zero or negative, such data layouts are however not
* commonly supported by GPU APIs.
* @see @ref MeshTools::isInterleaved()
*/
Short indexStride() const;
/**
* @brief Mesh indices
*
* For an indexed mesh, the view is guaranteed to be contiguous and its
* second dimension represents the actual data type (its size is equal
* to type size, even in case there's zero indices). For a non-indexed
* mesh, the returned view has a zero size in both dimensions.
* For an indexed mesh, the second dimension of the view is guaranteed
* to be contiguous and its size is equal to type size, even in case
* there's zero indices. For a non-indexed mesh, the returned view has
* a zero size in both dimensions. In rare cases the first dimension
* stride may be different from the index type size and even be zero or
* negative, such data layouts are however not commonly supported by
* GPU APIs.
*
* Use the templated overload below to get the indices in a concrete
* type.
@ -1138,13 +1207,16 @@ class MAGNUM_TRADE_EXPORT MeshData {
* @brief Mesh indices in a concrete type
*
* Expects that the mesh is indexed and that @p T corresponds to
* @ref indexType(). You can also use the non-templated
* @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(), @ref mutableIndices()
* @ref indexType(). In rare cases the first dimension stride may be
* different from the index type size and even be zero or negative,
* such data layouts are however not commonly supported by GPU APIs.
* You can also use the non-templated @ref indicesAsArray() accessor to
* get indices converted to a contiguous 32-bit array, but note that
* such operation involves extra allocation and data conversion.
* @see @ref isIndexed(), @ref attribute(), @ref mutableIndices(),
* @relativeref{Corrade,Containers::StridedArrayView::isContiguous()}
*/
template<class T> Containers::ArrayView<const T> indices() const;
template<class T> Containers::StridedArrayView1D<const T> indices() const;
/**
* @brief Mutable mesh indices in a concrete type
@ -1153,7 +1225,7 @@ class MAGNUM_TRADE_EXPORT MeshData {
* the mesh is mutable.
* @see @ref indexDataFlags()
*/
template<class T> Containers::ArrayView<T> mutableIndices();
template<class T> Containers::StridedArrayView1D<T> mutableIndices();
/**
* @brief Mesh vertex count
@ -1860,7 +1932,10 @@ class MAGNUM_TRADE_EXPORT MeshData {
UnsignedInt _indexCount, _vertexCount;
MeshPrimitive _primitive;
MeshIndexType _indexType;
/* 3 byte padding, reserved for 4-byte MeshIndexType */
Short _indexStride;
DataFlags _indexDataFlags, _vertexDataFlags;
/* 4 byte padding on 64bit */
const void* _importerState;
const char* _indices;
Containers::Array<MeshAttributeData> _attributes;
@ -1877,7 +1952,27 @@ namespace Implementation {
template<> constexpr MeshIndexType meshIndexTypeFor<UnsignedByte>() { return MeshIndexType::UnsignedByte; }
template<> constexpr MeshIndexType meshIndexTypeFor<UnsignedShort>() { return MeshIndexType::UnsignedShort; }
template<> constexpr MeshIndexType meshIndexTypeFor<UnsignedInt>() { return MeshIndexType::UnsignedInt; }
}
#ifndef DOXYGEN_GENERATING_OUTPUT
template<class T, class> MeshIndexData::MeshIndexData(const MeshIndexType type, T&& data) noexcept: _type{type} {
const Containers::ArrayView<const void> erased = data;
const std::size_t typeSize = meshIndexTypeSize(type);
CORRADE_ASSERT(erased.size()%typeSize == 0,
"Trade::MeshIndexData: view size" << erased.size() << "does not correspond to" << type, );
_data = Containers::StridedArrayView1D<const void>{erased, erased.size()/typeSize, std::ptrdiff_t(typeSize)};
}
#endif
constexpr MeshIndexData::MeshIndexData(const MeshIndexType type, const Containers::StridedArrayView1D<const void> data) noexcept:
_type{type},
/* The actual limitation is in MeshData, but better to have it caught here
already */
_data{(CORRADE_CONSTEXPR_ASSERT(data.stride() >= -32768 && data.stride() <= 32767,
"Trade::MeshIndexData: expected stride to fit into 16 bits but got" << data.stride()), data)}
{}
namespace Implementation {
/* Implicit mapping from a format to enum (1:1) */
template<class T> constexpr VertexFormat vertexFormatFor() {
/* C++ why there isn't an obvious way to do such a thing?! */
@ -2173,7 +2268,7 @@ template<class T> constexpr MeshAttributeData::MeshAttributeData(MeshAttribute n
UnsignedShort(data.size()[1])
} {}
template<class T> Containers::ArrayView<const T> MeshData::indices() const {
template<class T> Containers::StridedArrayView1D<const T> MeshData::indices() const {
CORRADE_ASSERT(isIndexed(),
"Trade::MeshData::indices(): the mesh is not indexed", {});
Containers::StridedArrayView2D<const char> data = indices();
@ -2182,10 +2277,10 @@ template<class T> Containers::ArrayView<const T> MeshData::indices() const {
#endif
CORRADE_ASSERT(Implementation::meshIndexTypeFor<T>() == _indexType,
"Trade::MeshData::indices(): indices are" << _indexType << "but requested" << Implementation::meshIndexTypeFor<T>(), {});
return Containers::arrayCast<1, const T>(data).asContiguous();
return Containers::arrayCast<1, const T>(data);
}
template<class T> Containers::ArrayView<T> MeshData::mutableIndices() {
template<class T> Containers::StridedArrayView1D<T> MeshData::mutableIndices() {
CORRADE_ASSERT(isIndexed(),
"Trade::MeshData::mutableIndices(): the mesh is not indexed", {});
Containers::StridedArrayView2D<char> data = mutableIndices();
@ -2194,7 +2289,7 @@ template<class T> Containers::ArrayView<T> MeshData::mutableIndices() {
#endif
CORRADE_ASSERT(Implementation::meshIndexTypeFor<T>() == _indexType,
"Trade::MeshData::mutableIndices(): indices are" << _indexType << "but requested" << Implementation::meshIndexTypeFor<T>(), {});
return Containers::arrayCast<1, T>(data).asContiguous();
return Containers::arrayCast<1, T>(data);
}
#ifndef CORRADE_NO_ASSERT

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

@ -44,12 +44,17 @@ struct MeshDataTest: TestSuite::Tester {
void customAttributeNameNotCustom();
void debugAttributeName();
void constructIndex();
void constructIndexTypeErased();
void constructIndexTypeErasedWrongSize();
void constructIndexContiguous();
void constructIndexStrided();
void constructIndexStridedWrongStride();
void constructIndexTypeErasedContiguous();
void constructIndexTypeErasedContiguousWrongSize();
void constructIndexTypeErasedStrided();
void constructIndexTypeErasedStridedWrongStride();
void constructIndex2D();
void constructIndex2DNotIndexed();
void constructIndex2DWrongSize();
void constructIndex2DWrongStride();
void constructIndex2DNonContiguous();
void constructIndexNullptr();
@ -90,6 +95,7 @@ struct MeshDataTest: TestSuite::Tester {
void constructIndexlessAttributeless();
void constructIndexlessAttributelessZeroVertices();
void constructSpecialIndexStrides();
void constructSpecialAttributeStrides();
void constructSpecialAttributeStridesImplementationSpecificVertexFormat();
@ -206,12 +212,17 @@ MeshDataTest::MeshDataTest() {
&MeshDataTest::customAttributeNameNotCustom,
&MeshDataTest::debugAttributeName,
&MeshDataTest::constructIndex,
&MeshDataTest::constructIndexTypeErased,
&MeshDataTest::constructIndexTypeErasedWrongSize,
&MeshDataTest::constructIndexContiguous,
&MeshDataTest::constructIndexStrided,
&MeshDataTest::constructIndexStridedWrongStride,
&MeshDataTest::constructIndexTypeErasedContiguous,
&MeshDataTest::constructIndexTypeErasedContiguousWrongSize,
&MeshDataTest::constructIndexTypeErasedStrided,
&MeshDataTest::constructIndexTypeErasedStridedWrongStride,
&MeshDataTest::constructIndex2D,
&MeshDataTest::constructIndex2DNotIndexed,
&MeshDataTest::constructIndex2DWrongSize,
&MeshDataTest::constructIndex2DWrongStride,
&MeshDataTest::constructIndex2DNonContiguous,
&MeshDataTest::constructIndexNullptr,
@ -253,6 +264,7 @@ MeshDataTest::MeshDataTest() {
&MeshDataTest::constructIndexlessAttributeless,
&MeshDataTest::constructIndexlessAttributelessZeroVertices,
&MeshDataTest::constructSpecialIndexStrides,
&MeshDataTest::constructSpecialAttributeStrides,
&MeshDataTest::constructSpecialAttributeStridesImplementationSpecificVertexFormat});
@ -450,51 +462,148 @@ constexpr UnsignedByte IndexBytes[]{25, 132, 3};
constexpr UnsignedShort IndexShorts[]{2575, 13224, 3};
constexpr UnsignedInt IndexInts[]{2110122, 132257, 3};
void MeshDataTest::constructIndex() {
void MeshDataTest::constructIndexContiguous() {
{
const UnsignedByte indexData[]{25, 132, 3};
MeshIndexData indices{indexData};
CORRADE_COMPARE(indices.type(), MeshIndexType::UnsignedByte);
CORRADE_COMPARE(indices.data().data(), indexData);
CORRADE_COMPARE(indices.data().size(), 3);
CORRADE_COMPARE(indices.data().stride(), 1);
constexpr MeshIndexData cindices{IndexBytes};
constexpr MeshIndexType type = cindices.type();
constexpr Containers::ArrayView<const void> data = cindices.data();
constexpr Containers::StridedArrayView1D<const void> data = cindices.data();
CORRADE_COMPARE(type, MeshIndexType::UnsignedByte);
CORRADE_COMPARE(data.data(), IndexBytes);
CORRADE_COMPARE(data.size(), 3);
CORRADE_COMPARE(data.stride(), 1);
} {
const UnsignedShort indexData[]{2575, 13224, 3};
MeshIndexData indices{indexData};
CORRADE_COMPARE(indices.type(), MeshIndexType::UnsignedShort);
CORRADE_COMPARE(indices.data().data(), indexData);
CORRADE_COMPARE(indices.data().size(), 3);
CORRADE_COMPARE(indices.data().stride(), 2);
constexpr MeshIndexData cindices{IndexShorts};
constexpr MeshIndexType type = cindices.type();
constexpr Containers::ArrayView<const void> data = cindices.data();
constexpr Containers::StridedArrayView1D<const void> data = cindices.data();
CORRADE_COMPARE(type, MeshIndexType::UnsignedShort);
CORRADE_COMPARE(data.data(), IndexShorts);
CORRADE_COMPARE(data.size(), 3);
CORRADE_COMPARE(data.stride(), 2);
} {
const UnsignedInt indexData[]{2110122, 132257, 3};
MeshIndexData indices{indexData};
CORRADE_COMPARE(indices.type(), MeshIndexType::UnsignedInt);
CORRADE_COMPARE(indices.data().data(), indexData);
CORRADE_COMPARE(indices.data().size(), 3);
CORRADE_COMPARE(indices.data().stride(), 4);
constexpr MeshIndexData cindices{IndexInts};
constexpr MeshIndexType type = cindices.type();
constexpr Containers::ArrayView<const void> data = cindices.data();
constexpr Containers::StridedArrayView1D<const void> data = cindices.data();
CORRADE_COMPARE(type, MeshIndexType::UnsignedInt);
CORRADE_COMPARE(data.data(), IndexInts);
CORRADE_COMPARE(data.size(), 3);
CORRADE_COMPARE(data.stride(), 4);
}
}
void MeshDataTest::constructIndexTypeErased() {
constexpr struct IndexStruct {
UnsignedByte byteIndex;
UnsignedShort shortIndex;
UnsignedInt intIndex;
} IndexStructData[3]{
{25, 2575, 2110122},
{132, 13224, 132257},
{3, 3, 3}
};
void MeshDataTest::constructIndexStrided() {
const IndexStruct data[3]{
{25, 2575, 2110122},
{132, 13224, 132257},
{3, 3, 3}
};
Containers::StridedArrayView1D<const IndexStruct> view = data;
{
MeshIndexData indices{view.slice(&IndexStruct::byteIndex)};
CORRADE_COMPARE(indices.type(), MeshIndexType::UnsignedByte);
CORRADE_COMPARE(indices.data().data(), &data[0].byteIndex);
CORRADE_COMPARE(indices.data().size(), 3);
CORRADE_COMPARE(indices.data().stride(), sizeof(IndexStruct));
constexpr MeshIndexData cindices{Containers::stridedArrayView(IndexStructData, &IndexStructData[0].byteIndex, 3, sizeof(IndexStruct))};
constexpr MeshIndexType type = cindices.type();
constexpr Containers::StridedArrayView1D<const void> data = cindices.data();
CORRADE_COMPARE(type, MeshIndexType::UnsignedByte);
CORRADE_COMPARE(data.data(), &IndexStructData[0].byteIndex);
CORRADE_COMPARE(data.size(), 3);
CORRADE_COMPARE(data.stride(), sizeof(IndexStruct));
} {
MeshIndexData indices{view.slice(&IndexStruct::shortIndex)};
CORRADE_COMPARE(indices.type(), MeshIndexType::UnsignedShort);
CORRADE_COMPARE(indices.data().data(), &data[0].shortIndex);
CORRADE_COMPARE(indices.data().size(), 3);
CORRADE_COMPARE(indices.data().stride(), sizeof(IndexStruct));
constexpr MeshIndexData cindices{Containers::stridedArrayView(IndexStructData, &IndexStructData[0].shortIndex, 3, sizeof(IndexStruct))};
constexpr MeshIndexType type = cindices.type();
constexpr Containers::StridedArrayView1D<const void> data = cindices.data();
CORRADE_COMPARE(type, MeshIndexType::UnsignedShort);
CORRADE_COMPARE(data.data(), &IndexStructData[0].shortIndex);
CORRADE_COMPARE(data.size(), 3);
CORRADE_COMPARE(data.stride(), sizeof(IndexStruct));
} {
MeshIndexData indices{view.slice(&IndexStruct::intIndex)};
CORRADE_COMPARE(indices.type(), MeshIndexType::UnsignedInt);
CORRADE_COMPARE(indices.data().data(), &data[0].intIndex);
CORRADE_COMPARE(indices.data().size(), 3);
CORRADE_COMPARE(indices.data().stride(), sizeof(IndexStruct));
constexpr MeshIndexData cindices{Containers::stridedArrayView(IndexStructData, &IndexStructData[0].intIndex, 3, sizeof(IndexStruct))};
constexpr MeshIndexType type = cindices.type();
constexpr Containers::StridedArrayView1D<const void> data = cindices.data();
CORRADE_COMPARE(type, MeshIndexType::UnsignedInt);
CORRADE_COMPARE(data.data(), &IndexStructData[0].intIndex);
CORRADE_COMPARE(data.size(), 3);
CORRADE_COMPARE(data.stride(), sizeof(IndexStruct));
}
}
void MeshDataTest::constructIndexStridedWrongStride() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
char toomuch[2*(32768 + 1)];
/* These should be fine */
MeshIndexData{Containers::StridedArrayView1D<UnsignedByte>{Containers::arrayCast<UnsignedByte>(toomuch), 2, 32767}};
MeshIndexData{Containers::StridedArrayView1D<UnsignedByte>{Containers::arrayCast<UnsignedByte>(toomuch), 2, 32768}.flipped<0>()};
std::ostringstream out;
Error redirectError{&out};
MeshIndexData{Containers::StridedArrayView1D<UnsignedByte>{Containers::arrayCast<UnsignedByte>(toomuch), 2, 32768}};
MeshIndexData{Containers::StridedArrayView1D<UnsignedByte>{Containers::arrayCast<UnsignedByte>(toomuch), 2, 32769}.flipped<0>()};
CORRADE_COMPARE(out.str(),
"Trade::MeshIndexData: expected stride to fit into 16 bits but got 32768\n"
"Trade::MeshIndexData: expected stride to fit into 16 bits but got -32769\n");
}
void MeshDataTest::constructIndexTypeErasedContiguous() {
const char indexData[3*2]{};
MeshIndexData indices{MeshIndexType::UnsignedShort, indexData};
CORRADE_COMPARE(indices.type(), MeshIndexType::UnsignedShort);
CORRADE_VERIFY(indices.data().data() == indexData);
CORRADE_COMPARE(indices.data().data(), indexData);
CORRADE_COMPARE(indices.data().size(), 3);
CORRADE_COMPARE(indices.data().stride(), 2);
}
void MeshDataTest::constructIndexTypeErasedWrongSize() {
void MeshDataTest::constructIndexTypeErasedContiguousWrongSize() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
@ -507,22 +616,71 @@ void MeshDataTest::constructIndexTypeErasedWrongSize() {
CORRADE_COMPARE(out.str(), "Trade::MeshIndexData: view size 6 does not correspond to MeshIndexType::UnsignedInt\n");
}
constexpr const char IndexData[3*4]{};
void MeshDataTest::constructIndexTypeErasedStrided() {
const char indexData[3*4]{};
MeshIndexData indices{MeshIndexType::UnsignedShort, {indexData, 3, 4}};
CORRADE_COMPARE(indices.type(), MeshIndexType::UnsignedShort);
CORRADE_VERIFY(indices.data().data() == indexData);
CORRADE_COMPARE(indices.data().size(), 3);
CORRADE_COMPARE(indices.data().stride(), 4);
constexpr MeshIndexData cindices{MeshIndexType::UnsignedShort, {IndexData, 3, 4}};
constexpr MeshIndexType type = cindices.type();
constexpr Containers::StridedArrayView1D<const void> data = cindices.data();
CORRADE_COMPARE(type, MeshIndexType::UnsignedShort);
CORRADE_COMPARE(data.data(), IndexData);
CORRADE_COMPARE(data.size(), 3);
CORRADE_COMPARE(data.stride(), 4);
}
void MeshDataTest::constructIndexTypeErasedStridedWrongStride() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
char toomuch[2*(32768 + 1)]{};
/* These should be fine */
MeshIndexData{MeshIndexType::UnsignedByte, Containers::StridedArrayView1D<const void>{toomuch, 2, 32767}};
MeshIndexData{MeshIndexType::UnsignedByte, Containers::StridedArrayView1D<UnsignedByte>{Containers::arrayCast<UnsignedByte>(toomuch), 2, 32768}.flipped<0>()};
std::ostringstream out;
Error redirectError{&out};
MeshIndexData{MeshIndexType::UnsignedByte, Containers::StridedArrayView1D<const void>{toomuch, 2, 32768}};
MeshIndexData{MeshIndexType::UnsignedByte, Containers::StridedArrayView1D<UnsignedByte>{Containers::arrayCast<UnsignedByte>(toomuch), 2, 32769}.flipped<0>()};
CORRADE_COMPARE(out.str(),
"Trade::MeshIndexData: expected stride to fit into 16 bits but got 32768\n"
"Trade::MeshIndexData: expected stride to fit into 16 bits but got -32769\n");
}
void MeshDataTest::constructIndex2D() {
const IndexStruct data[3]{
{25, 2575, 2110122},
{132, 13224, 132257},
{3, 3, 3}
};
Containers::StridedArrayView1D<const IndexStruct> view = data;
{
const UnsignedByte indexData[]{25, 132, 3};
MeshIndexData indices{Containers::arrayCast<2, const char>(Containers::stridedArrayView(indexData))};
MeshIndexData indices{Containers::arrayCast<2, const char>(view.slice(&IndexStruct::byteIndex))};
CORRADE_COMPARE(indices.type(), MeshIndexType::UnsignedByte);
CORRADE_COMPARE(indices.data().data(), indexData);
CORRADE_COMPARE(indices.data().data(), &data[0].byteIndex);
CORRADE_COMPARE(indices.data().size(), 3);
CORRADE_COMPARE(indices.data().stride(), sizeof(IndexStruct));
} {
const UnsignedShort indexData[]{2575, 13224, 3};
MeshIndexData indices{Containers::arrayCast<2, const char>(Containers::stridedArrayView(indexData))};
MeshIndexData indices{Containers::arrayCast<2, const char>(view.slice(&IndexStruct::shortIndex))};
CORRADE_COMPARE(indices.type(), MeshIndexType::UnsignedShort);
CORRADE_COMPARE(indices.data().data(), indexData);
CORRADE_COMPARE(indices.data().data(), &data[0].shortIndex);
CORRADE_COMPARE(indices.data().size(), 3);
CORRADE_COMPARE(indices.data().stride(), sizeof(IndexStruct));
} {
const UnsignedInt indexData[]{2110122, 132257, 3};
MeshIndexData indices{Containers::arrayCast<2, const char>(Containers::stridedArrayView(indexData))};
MeshIndexData indices{Containers::arrayCast<2, const char>(view.slice(&IndexStruct::intIndex))};
CORRADE_COMPARE(indices.type(), MeshIndexType::UnsignedInt);
CORRADE_COMPARE(indices.data().data(), indexData);
CORRADE_COMPARE(indices.data().data(), &data[0].intIndex);
CORRADE_COMPARE(indices.data().size(), 3);
CORRADE_COMPARE(indices.data().stride(), sizeof(IndexStruct));
}
}
@ -545,6 +703,26 @@ void MeshDataTest::constructIndex2DWrongSize() {
CORRADE_COMPARE(out.str(), "Trade::MeshIndexData: expected index type size 1, 2 or 4 but got 3\n");
}
void MeshDataTest::constructIndex2DWrongStride() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
char toomuch[2*(32768 + 1)];
/* These should be fine */
MeshIndexData{Containers::StridedArrayView2D<char>{toomuch, {2, 1}, {32767, 1}}};
MeshIndexData{Containers::StridedArrayView2D<char>{toomuch, {2, 1}, {32768, 1}}.flipped<0>()};
std::ostringstream out;
Error redirectError{&out};
MeshIndexData{Containers::StridedArrayView2D<char>{toomuch, {2, 1}, {32768, 1}}};
MeshIndexData{Containers::StridedArrayView2D<char>{toomuch, {2, 1}, {32769, 1}}.flipped<0>()};
CORRADE_COMPARE(out.str(),
"Trade::MeshIndexData: expected stride to fit into 16 bits but got 32768\n"
"Trade::MeshIndexData: expected stride to fit into 16 bits but got -32769\n");
}
void MeshDataTest::constructIndex2DNonContiguous() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
@ -552,10 +730,13 @@ void MeshDataTest::constructIndex2DNonContiguous() {
const char data[3*4]{};
/* This should be fine */
MeshIndexData{Containers::StridedArrayView2D<const char>{data, {3, 2}, {4, 1}}};
std::ostringstream out;
Error redirectError{&out};
MeshIndexData{Containers::StridedArrayView2D<const char>{data, {3, 2}, {4, 2}}};
CORRADE_COMPARE(out.str(), "Trade::MeshIndexData: view is not contiguous\n");
CORRADE_COMPARE(out.str(), "Trade::MeshIndexData: second view dimension is not contiguous\n");
}
void MeshDataTest::constructIndexNullptr() {
@ -1649,6 +1830,102 @@ void MeshDataTest::constructIndexlessAttributelessZeroVertices() {
CORRADE_COMPARE(data.attributeCount(), 0);
}
void MeshDataTest::constructSpecialIndexStrides() {
/* Every second index */
{
Containers::Array<char> indexData{sizeof(UnsignedShort)*8};
Containers::StridedArrayView1D<UnsignedShort> indices = Containers::arrayCast<UnsignedShort>(indexData);
Utility::copy({1, 0, 2, 0, 3, 0, 4, 0}, indices);
MeshData mesh{MeshPrimitive::Points, std::move(indexData), MeshIndexData{indices.every(2)}, 1};
CORRADE_COMPARE(mesh.indexStride(), 4);
/* Type-erased access with a cast later */
CORRADE_COMPARE_AS((Containers::arrayCast<1, const UnsignedShort>(mesh.indices())),
Containers::arrayView<UnsignedShort>({1, 2, 3, 4}),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS((Containers::arrayCast<1, UnsignedShort>(mesh.mutableIndices())),
Containers::stridedArrayView<UnsignedShort>({1, 2, 3, 4}),
TestSuite::Compare::Container);
/* Typed access */
CORRADE_COMPARE_AS(mesh.indices<UnsignedShort>(),
Containers::arrayView<UnsignedShort>({1, 2, 3, 4}),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(mesh.mutableIndices<UnsignedShort>(),
Containers::stridedArrayView<UnsignedShort>({1, 2, 3, 4}),
TestSuite::Compare::Container);
/* Convenience accessor. This uses the indicesInto() internally so it
verifies both. */
CORRADE_COMPARE_AS(mesh.indicesAsArray(),
Containers::arrayView<UnsignedInt>({1, 2, 3, 4}),
TestSuite::Compare::Container);
/* Zero stride. Not sure how useful like this. */
} {
Containers::Array<char> indexData{sizeof(UnsignedShort)};
Containers::StridedArrayView1D<UnsignedShort> indices = Containers::arrayCast<UnsignedShort>(indexData);
indices[0] = 15;
MeshData mesh{MeshPrimitive::Points, std::move(indexData), MeshIndexData{indices.broadcasted<0>(4)}, 1};
CORRADE_COMPARE(mesh.indexStride(), 0);
/* Type-erased access with a cast later */
CORRADE_COMPARE_AS((Containers::arrayCast<1, const UnsignedShort>(mesh.indices())),
Containers::arrayView<UnsignedShort>({15, 15, 15, 15}),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS((Containers::arrayCast<1, UnsignedShort>(mesh.mutableIndices())),
Containers::stridedArrayView<UnsignedShort>({15, 15, 15, 15}),
TestSuite::Compare::Container);
/* Typed access */
CORRADE_COMPARE_AS(mesh.indices<UnsignedShort>(),
Containers::arrayView<UnsignedShort>({15, 15, 15, 15}),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(mesh.mutableIndices<UnsignedShort>(),
Containers::stridedArrayView<UnsignedShort>({15, 15, 15, 15}),
TestSuite::Compare::Container);
/* The convenience accessor should work as well, as it consumes output
of the type-erased one */
CORRADE_COMPARE_AS(mesh.indicesAsArray(),
Containers::arrayView<UnsignedInt>({15, 15, 15, 15}),
TestSuite::Compare::Container);
/* Negative stride */
} {
Containers::Array<char> indexData{sizeof(UnsignedShort)*4};
Containers::StridedArrayView1D<UnsignedShort> indices = Containers::arrayCast<UnsignedShort>(indexData);
Utility::copy({1, 2, 3, 4}, indices);
MeshData mesh{MeshPrimitive::Points, std::move(indexData), MeshIndexData{indices.flipped<0>()}, 1};
CORRADE_COMPARE(mesh.indexStride(), -2);
/* Type-erased access with a cast later */
CORRADE_COMPARE_AS((Containers::arrayCast<1, const UnsignedShort>(mesh.indices())),
Containers::arrayView<UnsignedShort>({4, 3, 2, 1}),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS((Containers::arrayCast<1, UnsignedShort>(mesh.mutableIndices())),
Containers::stridedArrayView<UnsignedShort>({4, 3, 2, 1}),
TestSuite::Compare::Container);
/* Typed access */
CORRADE_COMPARE_AS(mesh.indices<UnsignedShort>(),
Containers::arrayView<UnsignedShort>({4, 3, 2, 1}),
TestSuite::Compare::Container);
CORRADE_COMPARE_AS(mesh.mutableIndices<UnsignedShort>(),
Containers::stridedArrayView<UnsignedShort>({4, 3, 2, 1}),
TestSuite::Compare::Container);
/* The convenience accessor should work as well, as it consumes output
of the type-erased one */
CORRADE_COMPARE_AS(mesh.indicesAsArray(),
Containers::arrayView<UnsignedInt>({4, 3, 2, 1}),
TestSuite::Compare::Container);
}
}
void MeshDataTest::constructSpecialAttributeStrides() {
Containers::Array<char> vertexData{sizeof(UnsignedShort)*5};
Containers::StridedArrayView1D<UnsignedShort> vertices = Containers::arrayCast<UnsignedShort>(vertexData);
@ -1769,9 +2046,11 @@ void MeshDataTest::constructIndicesNotContained() {
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
Containers::Array<char> indexData{reinterpret_cast<char*>(0xbadda9), 6, [](char*, std::size_t){}};
const Containers::Array<char> indexData{reinterpret_cast<char*>(0xbadda9), 6, [](char*, std::size_t){}};
Containers::Array<char> sameIndexDataButMovable{reinterpret_cast<char*>(0xbadda9), 6, [](char*, std::size_t){}};
Containers::ArrayView<UnsignedShort> indexDataSlightlyOut{reinterpret_cast<UnsignedShort*>(0xbaddaa), 3};
Containers::ArrayView<UnsignedShort> indexDataOut{reinterpret_cast<UnsignedShort*>(0xdead), 3};
Containers::StridedArrayView1D<UnsignedShort> indexDataStridedOut{{reinterpret_cast<UnsignedShort*>(0xbadda9), 6}, 3, 4};
std::ostringstream out;
Error redirectError{&out};
@ -1779,20 +2058,29 @@ void MeshDataTest::constructIndicesNotContained() {
MeshData{MeshPrimitive::Triangles, {}, indexData, MeshIndexData{indexDataSlightlyOut}, 1};
/* Second a view that's in a completely different location */
MeshData{MeshPrimitive::Triangles, {}, indexData, MeshIndexData{indexDataOut}, 1};
/* A strided index array which would pass if stride wasn't taken into
account */
MeshData{MeshPrimitive::Triangles, {}, indexData, MeshIndexData{indexDataStridedOut}, 1};
/* Empty view which however begins outside */
MeshData{MeshPrimitive::Triangles, {}, indexData, MeshIndexData{indexDataSlightlyOut.slice(3, 3)}, 1};
/* Verify the owning constructor does the checks as well */
MeshData{MeshPrimitive::Triangles, std::move(indexData), MeshIndexData{indexDataOut}, 1};
MeshData{MeshPrimitive::Triangles, std::move(sameIndexDataButMovable), MeshIndexData{indexDataOut}, 1};
/* If we have no data at all, it doesn't try to dereference them but still
checks properly */
MeshData{MeshPrimitive::Triangles, nullptr, MeshIndexData{indexDataOut}, 1};
/* And the final boss, negative strides. Only caught if the element size
gets properly added to the larger offset, not just the "end". */
MeshData{MeshPrimitive::Triangles, {}, indexData, MeshIndexData{stridedArrayView(indexDataSlightlyOut).flipped<0>()}, 1};
CORRADE_COMPARE(out.str(),
"Trade::MeshData: indices [0xbaddaa:0xbaddb0] are not contained in passed indexData array [0xbadda9:0xbaddaf]\n"
"Trade::MeshData: indices [0xdead:0xdeb3] are not contained in passed indexData array [0xbadda9:0xbaddaf]\n"
"Trade::MeshData: indices [0xbadda9:0xbaddb3] are not contained in passed indexData array [0xbadda9:0xbaddaf]\n"
/* This scenario is invalid, just have it here for the record */
"Trade::MeshData: indexData passed for a non-indexed mesh\n"
"Trade::MeshData: indices [0xdead:0xdeb3] are not contained in passed indexData array [0xbadda9:0xbaddaf]\n"
"Trade::MeshData: indices [0xdead:0xdeb3] are not contained in passed indexData array [0x0:0x0]\n");
"Trade::MeshData: indices [0xdead:0xdeb3] are not contained in passed indexData array [0x0:0x0]\n"
"Trade::MeshData: indices [0xbaddaa:0xbaddb0] are not contained in passed indexData array [0xbadda9:0xbaddaf]\n");
}
void MeshDataTest::constructAttributeNotContained() {
@ -2936,6 +3224,7 @@ void MeshDataTest::indicesNotIndexed() {
data.indexCount();
data.indexType();
data.indexOffset();
data.indexStride();
data.indices<UnsignedInt>();
data.mutableIndices<UnsignedShort>();
data.indicesAsArray();
@ -2945,6 +3234,7 @@ void MeshDataTest::indicesNotIndexed() {
"Trade::MeshData::indexCount(): the mesh is not indexed\n"
"Trade::MeshData::indexType(): the mesh is not indexed\n"
"Trade::MeshData::indexOffset(): the mesh is not indexed\n"
"Trade::MeshData::indexStride(): the mesh is not indexed\n"
"Trade::MeshData::indices(): the mesh is not indexed\n"
"Trade::MeshData::mutableIndices(): the mesh is not indexed\n"
"Trade::MeshData::indicesAsArray(): the mesh is not indexed\n"

Loading…
Cancel
Save