mirror of https://github.com/mosra/magnum.git
Browse Source
Apmong other things where it's useful for end users as a more convenient alternative to recreating the MeshData by hand, I need to use this inside the transform() utilities to preserve all index buffer properties without copypasting nasty code everywhere.pull/547/head
6 changed files with 933 additions and 0 deletions
@ -0,0 +1,216 @@ |
|||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||||
|
2020, 2021 Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "FilterAttributes.h" |
||||||
|
|
||||||
|
#include <Corrade/Containers/ArrayView.h> |
||||||
|
#include <Corrade/Containers/GrowableArray.h> |
||||||
|
|
||||||
|
#include "Magnum/Trade/MeshData.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace MeshTools { |
||||||
|
|
||||||
|
namespace { |
||||||
|
|
||||||
|
bool hasAttribute(const Containers::ArrayView<const Trade::MeshAttribute> attributes, const Trade::MeshAttribute attribute) { |
||||||
|
for(const Trade::MeshAttribute i: attributes) |
||||||
|
if(i == attribute) return true; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/** @todo drop this insanity in favor of a BitArray */ |
||||||
|
bool hasAttribute(const Containers::ArrayView<const UnsignedInt> attributes, const UnsignedInt attribute) { |
||||||
|
for(const UnsignedInt i: attributes) |
||||||
|
if(i == attribute) return true; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
Trade::MeshData filterOnlyAttributes(const Trade::MeshData& data, const Containers::ArrayView<const Trade::MeshAttribute> attributes) { |
||||||
|
/* Not asserting here for existence of attributes since that'd be another
|
||||||
|
O(n^2) operation */ |
||||||
|
/** @todo but that's not consistent with the ID-based variant, or maybe do
|
||||||
|
this via a BitArray in the second loop and check that all attributes |
||||||
|
got referenced afterwards? */ |
||||||
|
|
||||||
|
/* Pick just attributes from the list */ |
||||||
|
Containers::Array<Trade::MeshAttributeData> filtered; |
||||||
|
arrayReserve(filtered, data.attributeCount()); |
||||||
|
for(UnsignedInt i = 0; i != data.attributeCount(); ++i) { |
||||||
|
if(hasAttribute(attributes, data.attributeName(i))) |
||||||
|
arrayAppend(filtered, data.attributeData(i)); |
||||||
|
} |
||||||
|
|
||||||
|
/* Convert back to a default deleter to make this usable in plugins */ |
||||||
|
arrayShrink(filtered, DefaultInit); |
||||||
|
|
||||||
|
/* Can't do just Trade::MeshIndexData{data.indices()} as that would discard
|
||||||
|
implementation-specific types. And can't do |
||||||
|
Trade::meshIndexData{data.indexType(), view} |
||||||
|
because asking for index type would assert on non-indexed meshes. */ |
||||||
|
Trade::MeshIndexData indices; |
||||||
|
if(data.isIndexed()) indices = Trade::MeshIndexData{ |
||||||
|
data.indexType(), |
||||||
|
Containers::StridedArrayView1D<const void>{ |
||||||
|
data.indexData(), |
||||||
|
data.indexData().data() + data.indexOffset(), |
||||||
|
data.indexCount(), |
||||||
|
data.indexStride()}}; |
||||||
|
|
||||||
|
return Trade::MeshData{data.primitive(), |
||||||
|
{}, data.indexData(), indices, |
||||||
|
{}, data.vertexData(), std::move(filtered), |
||||||
|
data.vertexCount()}; |
||||||
|
} |
||||||
|
|
||||||
|
Trade::MeshData filterOnlyAttributes(const Trade::MeshData& data, std::initializer_list<Trade::MeshAttribute> attributes) { |
||||||
|
return filterOnlyAttributes(data, Containers::arrayView(attributes)); |
||||||
|
} |
||||||
|
|
||||||
|
Trade::MeshData filterOnlyAttributes(const Trade::MeshData& data, const Containers::ArrayView<const UnsignedInt> attributes) { |
||||||
|
#ifndef CORRADE_NO_ASSERT |
||||||
|
for(const UnsignedInt i: attributes) CORRADE_ASSERT(i < data.attributeCount(), |
||||||
|
"MeshTools::filterOnlyAttributes(): index" << i << "out of range for" << data.attributeCount() << "attributes", |
||||||
|
(Trade::MeshData{MeshPrimitive{}, 0})); |
||||||
|
#endif |
||||||
|
|
||||||
|
/* Pick just attributes from the list */ |
||||||
|
Containers::Array<Trade::MeshAttributeData> filtered; |
||||||
|
arrayReserve(filtered, data.attributeCount()); |
||||||
|
for(UnsignedInt i = 0; i != data.attributeCount(); ++i) { |
||||||
|
if(hasAttribute(attributes, i)) |
||||||
|
arrayAppend(filtered, data.attributeData(i)); |
||||||
|
} |
||||||
|
|
||||||
|
/* Convert back to a default deleter to make this usable in plugins */ |
||||||
|
arrayShrink(filtered, DefaultInit); |
||||||
|
|
||||||
|
/* Can't do just Trade::MeshIndexData{data.indices()} as that would discard
|
||||||
|
implementation-specific types. And can't do |
||||||
|
Trade::meshIndexData{data.indexType(), view} |
||||||
|
because asking for index type would assert on non-indexed meshes. */ |
||||||
|
Trade::MeshIndexData indices; |
||||||
|
if(data.isIndexed()) indices = Trade::MeshIndexData{ |
||||||
|
data.indexType(), |
||||||
|
Containers::StridedArrayView1D<const void>{ |
||||||
|
data.indexData(), |
||||||
|
data.indexData().data() + data.indexOffset(), |
||||||
|
data.indexCount(), |
||||||
|
data.indexStride()}}; |
||||||
|
|
||||||
|
return Trade::MeshData{data.primitive(), |
||||||
|
{}, data.indexData(), indices, |
||||||
|
{}, data.vertexData(), std::move(filtered), |
||||||
|
data.vertexCount()}; |
||||||
|
} |
||||||
|
|
||||||
|
Trade::MeshData filterOnlyAttributes(const Trade::MeshData& data, std::initializer_list<UnsignedInt> attributes) { |
||||||
|
return filterOnlyAttributes(data, Containers::arrayView(attributes)); |
||||||
|
} |
||||||
|
|
||||||
|
Trade::MeshData filterExceptAttributes(const Trade::MeshData& data, const Containers::ArrayView<const Trade::MeshAttribute> attributes) { |
||||||
|
/* Not asserting here for existence of attributes since that'd be another
|
||||||
|
O(n^2) operation */ |
||||||
|
/** @todo but that's not consistent with the ID-based variant, or maybe do
|
||||||
|
this via a BitArray in the second loop and check that all attributes |
||||||
|
got referenced afterwards? */ |
||||||
|
|
||||||
|
/* Pick just attributes from the list */ |
||||||
|
Containers::Array<Trade::MeshAttributeData> filtered; |
||||||
|
arrayReserve(filtered, data.attributeCount()); |
||||||
|
for(UnsignedInt i = 0; i != data.attributeCount(); ++i) { |
||||||
|
if(!hasAttribute(attributes, data.attributeName(i))) |
||||||
|
arrayAppend(filtered, data.attributeData(i)); |
||||||
|
} |
||||||
|
|
||||||
|
/* Convert back to a default deleter to make this usable in plugins */ |
||||||
|
arrayShrink(filtered, DefaultInit); |
||||||
|
|
||||||
|
/* Can't do just Trade::MeshIndexData{data.indices()} as that would discard
|
||||||
|
implementation-specific types. And can't do |
||||||
|
Trade::meshIndexData{data.indexType(), view} |
||||||
|
because asking for index type would assert on non-indexed meshes. */ |
||||||
|
Trade::MeshIndexData indices; |
||||||
|
if(data.isIndexed()) indices = Trade::MeshIndexData{ |
||||||
|
data.indexType(), |
||||||
|
Containers::StridedArrayView1D<const void>{ |
||||||
|
data.indexData(), |
||||||
|
data.indexData().data() + data.indexOffset(), |
||||||
|
data.indexCount(), |
||||||
|
data.indexStride()}}; |
||||||
|
|
||||||
|
return Trade::MeshData{data.primitive(), |
||||||
|
{}, data.indexData(), indices, |
||||||
|
{}, data.vertexData(), std::move(filtered), |
||||||
|
data.vertexCount()}; |
||||||
|
} |
||||||
|
|
||||||
|
Trade::MeshData filterExceptAttributes(const Trade::MeshData& data, std::initializer_list<Trade::MeshAttribute> attributes) { |
||||||
|
return filterExceptAttributes(data, Containers::arrayView(attributes)); |
||||||
|
} |
||||||
|
|
||||||
|
Trade::MeshData filterExceptAttributes(const Trade::MeshData& data, const Containers::ArrayView<const UnsignedInt> attributes) { |
||||||
|
#ifndef CORRADE_NO_ASSERT |
||||||
|
for(const UnsignedInt i: attributes) CORRADE_ASSERT(i < data.attributeCount(), |
||||||
|
"MeshTools::filterExceptAttributes(): index" << i << "out of range for" << data.attributeCount() << "attributes", |
||||||
|
(Trade::MeshData{MeshPrimitive{}, 0})); |
||||||
|
#endif |
||||||
|
|
||||||
|
/* Pick just attributes from the list */ |
||||||
|
Containers::Array<Trade::MeshAttributeData> filtered; |
||||||
|
arrayReserve(filtered, data.attributeCount()); |
||||||
|
for(UnsignedInt i = 0; i != data.attributeCount(); ++i) { |
||||||
|
if(!hasAttribute(attributes, i)) |
||||||
|
arrayAppend(filtered, data.attributeData(i)); |
||||||
|
} |
||||||
|
|
||||||
|
/* Convert back to a default deleter to make this usable in plugins */ |
||||||
|
arrayShrink(filtered, DefaultInit); |
||||||
|
|
||||||
|
/* Can't do just Trade::MeshIndexData{data.indices()} as that would discard
|
||||||
|
implementation-specific types. And can't do |
||||||
|
Trade::meshIndexData{data.indexType(), view} |
||||||
|
because asking for index type would assert on non-indexed meshes. */ |
||||||
|
Trade::MeshIndexData indices; |
||||||
|
if(data.isIndexed()) indices = Trade::MeshIndexData{ |
||||||
|
data.indexType(), |
||||||
|
Containers::StridedArrayView1D<const void>{ |
||||||
|
data.indexData(), |
||||||
|
data.indexData().data() + data.indexOffset(), |
||||||
|
data.indexCount(), |
||||||
|
data.indexStride()}}; |
||||||
|
|
||||||
|
return Trade::MeshData{data.primitive(), |
||||||
|
{}, data.indexData(), indices, |
||||||
|
{}, data.vertexData(), std::move(filtered), |
||||||
|
data.vertexCount()}; |
||||||
|
} |
||||||
|
|
||||||
|
Trade::MeshData filterExceptAttributes(const Trade::MeshData& data, std::initializer_list<UnsignedInt> attributes) { |
||||||
|
return filterExceptAttributes(data, Containers::arrayView(attributes)); |
||||||
|
} |
||||||
|
|
||||||
|
}} |
||||||
@ -0,0 +1,140 @@ |
|||||||
|
#ifndef Magnum_MeshTools_FilterAttributes_h |
||||||
|
#define Magnum_MeshTools_FilterAttributes_h |
||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||||
|
2020, 2021 Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
/** @file
|
||||||
|
* @brief Function @ref Magnum::MeshTools::filterOnlyAttributes(), @ref Magnum::MeshTools::filterExceptAttributes() |
||||||
|
* @m_since_latest |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <initializer_list> |
||||||
|
|
||||||
|
#include "Magnum/MeshTools/visibility.h" |
||||||
|
#include "Magnum/Trade/Trade.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace MeshTools { |
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Filter a mesh to contain only the selected subset of named attributes |
||||||
|
@m_since_latest |
||||||
|
|
||||||
|
Returns a non-owning reference to the vertex and index buffer from @p data with |
||||||
|
only the attributes that are listed in @p attributes. The index buffer, if |
||||||
|
present, is left untouched. Attributes from the list that are not present in |
||||||
|
@p data are skipped. All duplicates of a listed attribute are kept --- if you |
||||||
|
want a different behavior, use the @ref filterOnlyAttributes(const Trade::MeshData&, Containers::ArrayView<const UnsignedInt>) |
||||||
|
overload and pick attributes by their IDs instead. |
||||||
|
|
||||||
|
This function only operates on the attribute metadata --- if you'd like to have |
||||||
|
the vertex data repacked to contain just the remaining attributes as well, pass |
||||||
|
the output to @ref interleave(const Trade::MeshData&, Containers::ArrayView<const Trade::MeshAttributeData>, InterleaveFlags) "interleave()" |
||||||
|
without @ref InterleaveFlag::PreserveInterleavedAttributes set. |
||||||
|
@see @ref reference() |
||||||
|
*/ |
||||||
|
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData filterOnlyAttributes(const Trade::MeshData& data, Containers::ArrayView<const Trade::MeshAttribute> attributes); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @overload |
||||||
|
* @m_since_latest |
||||||
|
*/ |
||||||
|
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData filterOnlyAttributes(const Trade::MeshData& data, std::initializer_list<Trade::MeshAttribute> attributes); |
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Filter a mesh to contain only the selected subset of attributes |
||||||
|
@m_since_latest |
||||||
|
|
||||||
|
Returns a non-owning reference to the vertex and index buffer from @p data with |
||||||
|
only the attribute IDs listed in @p attributes. IDs specified more than once |
||||||
|
don't result in given attribute being added multiple times. The index buffer, |
||||||
|
if present, is left untouched. All attribute IDs are expected to be smaller |
||||||
|
than @ref Trade::MeshData::attributeCount() const. |
||||||
|
|
||||||
|
This function only operates on the attribute metadata --- if you'd like to have |
||||||
|
the vertex data repacked to contain just the remaining attributes as well, pass |
||||||
|
the output to @ref interleave(const Trade::MeshData&, Containers::ArrayView<const Trade::MeshAttributeData>, InterleaveFlags) "interleave()" |
||||||
|
without @ref InterleaveFlag::PreserveInterleavedAttributes set. |
||||||
|
@see @ref reference() |
||||||
|
*/ |
||||||
|
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData filterOnlyAttributes(const Trade::MeshData& data, Containers::ArrayView<const UnsignedInt> attributes); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @overload |
||||||
|
* @m_since_latest |
||||||
|
*/ |
||||||
|
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData filterOnlyAttributes(const Trade::MeshData& data, std::initializer_list<UnsignedInt> attributes); |
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Filter a mesh to contain everything except the selected subset of named attributes |
||||||
|
@m_since_latest |
||||||
|
|
||||||
|
Returns a non-owning reference to the vertex and index buffer from @p data with |
||||||
|
only the attributes that are not listed in @p attributes. The index buffer, if |
||||||
|
present, is left untouched. Attributes from the list that are not present in |
||||||
|
@p data are skipped. All duplicates of a listed attribute are removed --- if |
||||||
|
you want a different behavior, use the @ref filterExceptAttributes(const Trade::MeshData&, Containers::ArrayView<const UnsignedInt>) |
||||||
|
overload and pick attributes by their IDs instead. If @p attributes is empty, |
||||||
|
the behavior is equivalent to @ref reference(). |
||||||
|
|
||||||
|
This function only operates on the attribute metadata --- if you'd like to have |
||||||
|
the vertex data repacked to contain just the remaining attributes as well, pass |
||||||
|
the output to @ref interleave(const Trade::MeshData&, Containers::ArrayView<const Trade::MeshAttributeData>, InterleaveFlags) "interleave()" |
||||||
|
without @ref InterleaveFlag::PreserveInterleavedAttributes set. |
||||||
|
*/ |
||||||
|
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData filterExceptAttributes(const Trade::MeshData& data, Containers::ArrayView<const Trade::MeshAttribute> attributes); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @overload |
||||||
|
* @m_since_latest |
||||||
|
*/ |
||||||
|
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData filterExceptAttributes(const Trade::MeshData& data, std::initializer_list<Trade::MeshAttribute> attributes); |
||||||
|
|
||||||
|
/**
|
||||||
|
@brief Filter a mesh to contain everything except the selected subset of attributes |
||||||
|
@m_since_latest |
||||||
|
|
||||||
|
Returns a non-owning reference to the vertex and index buffer from @p data with |
||||||
|
only the attribute IDs that are not listed in @p attributes. IDs specified |
||||||
|
multiple times behave like if specified just once. The index buffer, if |
||||||
|
present, is left untouched. All attribute IDs are expected to be smaller than |
||||||
|
@ref Trade::MeshData::attributeCount() const. If @p attributes is empty, the |
||||||
|
behavior is equivalent to @ref reference(). |
||||||
|
|
||||||
|
This function only operates on the attribute metadata --- if you'd like to have |
||||||
|
the vertex data repacked to contain just the remaining attributes as well, pass |
||||||
|
the output to @ref interleave(const Trade::MeshData&, Containers::ArrayView<const Trade::MeshAttributeData>, InterleaveFlags) "interleave()" |
||||||
|
without @ref InterleaveFlag::PreserveInterleavedAttributes set. |
||||||
|
*/ |
||||||
|
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData filterExceptAttributes(const Trade::MeshData& data, Containers::ArrayView<const UnsignedInt> attributes); |
||||||
|
|
||||||
|
/**
|
||||||
|
* @overload |
||||||
|
* @m_since_latest |
||||||
|
*/ |
||||||
|
MAGNUM_MESHTOOLS_EXPORT Trade::MeshData filterExceptAttributes(const Trade::MeshData& data, std::initializer_list<UnsignedInt> attributes); |
||||||
|
|
||||||
|
}} |
||||||
|
|
||||||
|
#endif |
||||||
@ -0,0 +1,570 @@ |
|||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||||
|
2020, 2021 Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <sstream> |
||||||
|
#include <Corrade/TestSuite/Tester.h> |
||||||
|
#include <Corrade/Utility/DebugStl.h> |
||||||
|
|
||||||
|
#include "Magnum/Math/Vector4.h" |
||||||
|
#include "Magnum/MeshTools/FilterAttributes.h" |
||||||
|
#include "Magnum/Trade/MeshData.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace MeshTools { namespace Test { namespace { |
||||||
|
|
||||||
|
struct FilterAttributesTest: TestSuite::Tester { |
||||||
|
explicit FilterAttributesTest(); |
||||||
|
|
||||||
|
void filterOnlyAttributeNames(); |
||||||
|
void filterOnlyAttributeNamesNoIndexData(); |
||||||
|
void filterOnlyAttributeNamesNoAttributeData(); |
||||||
|
|
||||||
|
void filterOnlyAttributeIds(); |
||||||
|
void filterOnlyAttributeIdsOutOfBounds(); |
||||||
|
void filterOnlyAttributeIdsNoIndexData(); |
||||||
|
void filterOnlyAttributeIdsNoAttributeData(); |
||||||
|
|
||||||
|
void filterExceptAttributeNames(); |
||||||
|
void filterExceptAttributeNamesNoIndexData(); |
||||||
|
void filterExceptAttributeNamesNoAttributeData(); |
||||||
|
|
||||||
|
void filterExceptAttributeIds(); |
||||||
|
void filterExceptAttributeIdsOutOfBounds(); |
||||||
|
void filterExceptAttributeIdsNoIndexData(); |
||||||
|
void filterExceptAttributeIdsNoAttributeData(); |
||||||
|
}; |
||||||
|
|
||||||
|
const struct { |
||||||
|
const char* name; |
||||||
|
MeshIndexType indexType; |
||||||
|
} ImplementationSpecificIndexTypeData[]{ |
||||||
|
{"", MeshIndexType::UnsignedShort}, |
||||||
|
{"implementation-specific index type", meshIndexTypeWrap(0xcaca)} |
||||||
|
}; |
||||||
|
|
||||||
|
FilterAttributesTest::FilterAttributesTest() { |
||||||
|
addInstancedTests({&FilterAttributesTest::filterOnlyAttributeNames}, |
||||||
|
Containers::arraySize(ImplementationSpecificIndexTypeData)); |
||||||
|
|
||||||
|
addTests({&FilterAttributesTest::filterOnlyAttributeNamesNoIndexData, |
||||||
|
&FilterAttributesTest::filterOnlyAttributeNamesNoAttributeData}); |
||||||
|
|
||||||
|
addInstancedTests({&FilterAttributesTest::filterOnlyAttributeIds}, |
||||||
|
Containers::arraySize(ImplementationSpecificIndexTypeData)); |
||||||
|
|
||||||
|
addTests({&FilterAttributesTest::filterOnlyAttributeIdsOutOfBounds, |
||||||
|
&FilterAttributesTest::filterOnlyAttributeIdsNoIndexData, |
||||||
|
&FilterAttributesTest::filterOnlyAttributeIdsNoAttributeData}); |
||||||
|
|
||||||
|
addInstancedTests({&FilterAttributesTest::filterExceptAttributeNames}, |
||||||
|
Containers::arraySize(ImplementationSpecificIndexTypeData)); |
||||||
|
|
||||||
|
addTests({&FilterAttributesTest::filterExceptAttributeNamesNoIndexData, |
||||||
|
&FilterAttributesTest::filterExceptAttributeNamesNoAttributeData}); |
||||||
|
|
||||||
|
addInstancedTests({&FilterAttributesTest::filterExceptAttributeIds}, |
||||||
|
Containers::arraySize(ImplementationSpecificIndexTypeData)); |
||||||
|
|
||||||
|
addTests({&FilterAttributesTest::filterExceptAttributeIdsOutOfBounds, |
||||||
|
&FilterAttributesTest::filterExceptAttributeIdsNoIndexData, |
||||||
|
&FilterAttributesTest::filterExceptAttributeIdsNoAttributeData}); |
||||||
|
} |
||||||
|
|
||||||
|
struct Vertex { |
||||||
|
Vector3 position; |
||||||
|
Vector4 tangent; |
||||||
|
Vector2 textureCoordinates1, textureCoordinates2; |
||||||
|
}; |
||||||
|
|
||||||
|
void FilterAttributesTest::filterOnlyAttributeNames() { |
||||||
|
auto&& data = ImplementationSpecificIndexTypeData[testCaseInstanceId()]; |
||||||
|
setTestCaseDescription(data.name); |
||||||
|
|
||||||
|
Containers::Array<char> indexData{5*sizeof(UnsignedShort)}; |
||||||
|
Containers::StridedArrayView1D<UnsignedShort> indices = Containers::arrayCast<UnsignedShort>(indexData); |
||||||
|
Containers::Array<char> vertexData{3*sizeof(Vertex)}; |
||||||
|
Containers::StridedArrayView1D<Vertex> vertices = Containers::arrayCast<Vertex>(vertexData); |
||||||
|
|
||||||
|
Trade::MeshData mesh{MeshPrimitive::TriangleStrip, |
||||||
|
std::move(indexData), Trade::MeshIndexData{data.indexType, indices}, |
||||||
|
std::move(vertexData), { |
||||||
|
Trade::MeshAttributeData{Trade::MeshAttribute::Position, vertices.slice(&Vertex::position)}, |
||||||
|
Trade::MeshAttributeData{Trade::MeshAttribute::Tangent, vertices.slice(&Vertex::tangent)}, |
||||||
|
Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates, vertices.slice(&Vertex::textureCoordinates1)}, |
||||||
|
Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates, vertices.slice(&Vertex::textureCoordinates2)}, |
||||||
|
}}; |
||||||
|
|
||||||
|
Trade::MeshData filtered = filterOnlyAttributes(mesh, { |
||||||
|
Trade::MeshAttribute::Position, |
||||||
|
Trade::MeshAttribute::Normal, /* not present, ignored */ |
||||||
|
Trade::MeshAttribute::TextureCoordinates, /* present twice */ |
||||||
|
}); |
||||||
|
CORRADE_COMPARE(filtered.primitive(), MeshPrimitive::TriangleStrip); |
||||||
|
|
||||||
|
CORRADE_VERIFY(filtered.isIndexed()); |
||||||
|
CORRADE_COMPARE(filtered.indexCount(), 5); |
||||||
|
CORRADE_COMPARE(filtered.indexType(), data.indexType); |
||||||
|
CORRADE_COMPARE(filtered.indexData().data(), indices.data()); |
||||||
|
CORRADE_COMPARE(filtered.indexDataFlags(), Trade::DataFlags{}); |
||||||
|
|
||||||
|
CORRADE_COMPARE(filtered.vertexCount(), 3); |
||||||
|
CORRADE_COMPARE(filtered.vertexData().data(), vertices.data()); |
||||||
|
CORRADE_COMPARE(filtered.vertexDataFlags(), Trade::DataFlags{}); |
||||||
|
|
||||||
|
/* Testing just the offset if it matches expectations, the
|
||||||
|
MeshAttributeData is copied directly so no metadata should get lost */ |
||||||
|
CORRADE_COMPARE(filtered.attributeCount(), 3); |
||||||
|
CORRADE_COMPARE(filtered.attributeName(0), Trade::MeshAttribute::Position); |
||||||
|
CORRADE_COMPARE(filtered.attributeOffset(0), offsetof(Vertex, position)); |
||||||
|
CORRADE_COMPARE(filtered.attributeName(1), Trade::MeshAttribute::TextureCoordinates); |
||||||
|
CORRADE_COMPARE(filtered.attributeOffset(1), offsetof(Vertex, textureCoordinates1)); |
||||||
|
CORRADE_COMPARE(filtered.attributeName(2), Trade::MeshAttribute::TextureCoordinates); |
||||||
|
CORRADE_COMPARE(filtered.attributeOffset(2), offsetof(Vertex, textureCoordinates2)); |
||||||
|
|
||||||
|
/* The attribute data should not be a growable array to make this usable in
|
||||||
|
plugins */ |
||||||
|
Containers::Array<Trade::MeshAttributeData> attributeData = filtered.releaseAttributeData(); |
||||||
|
CORRADE_VERIFY(!attributeData.deleter()); |
||||||
|
} |
||||||
|
|
||||||
|
void FilterAttributesTest::filterOnlyAttributeNamesNoIndexData() { |
||||||
|
/* A trivial subset of filterOnlyAttributeNames() testing it doesn't blow
|
||||||
|
up if the mesh is not indexed */ |
||||||
|
|
||||||
|
Containers::Array<char> vertexData{3*sizeof(Vertex)}; |
||||||
|
Containers::StridedArrayView1D<Vertex> vertices = Containers::arrayCast<Vertex>(vertexData); |
||||||
|
|
||||||
|
Trade::MeshData mesh{MeshPrimitive::TriangleFan, |
||||||
|
std::move(vertexData), { |
||||||
|
Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates, vertices.slice(&Vertex::textureCoordinates1)} |
||||||
|
}}; |
||||||
|
|
||||||
|
Trade::MeshData filtered = filterOnlyAttributes(mesh, { |
||||||
|
Trade::MeshAttribute::TextureCoordinates |
||||||
|
}); |
||||||
|
CORRADE_COMPARE(filtered.primitive(), MeshPrimitive::TriangleFan); |
||||||
|
|
||||||
|
CORRADE_VERIFY(!filtered.isIndexed()); |
||||||
|
/* Consistent with behavior in reference() for index-less meshes */ |
||||||
|
CORRADE_COMPARE(filtered.indexDataFlags(), Trade::DataFlags{}); |
||||||
|
|
||||||
|
CORRADE_COMPARE(filtered.vertexCount(), 3); |
||||||
|
CORRADE_COMPARE(filtered.vertexData().data(), vertices.data()); |
||||||
|
CORRADE_COMPARE(filtered.vertexDataFlags(), Trade::DataFlags{}); |
||||||
|
|
||||||
|
/* Testing just the offset if it matches expectations, the
|
||||||
|
MeshAttributeData is copied directly so no metadata should get lost */ |
||||||
|
CORRADE_COMPARE(filtered.attributeCount(), 1); |
||||||
|
CORRADE_COMPARE(filtered.attributeName(0), Trade::MeshAttribute::TextureCoordinates); |
||||||
|
CORRADE_COMPARE(filtered.attributeOffset(0), offsetof(Vertex, textureCoordinates1)); |
||||||
|
} |
||||||
|
|
||||||
|
void FilterAttributesTest::filterOnlyAttributeNamesNoAttributeData() { |
||||||
|
Containers::Array<char> indexData{5*sizeof(UnsignedShort)}; |
||||||
|
Containers::StridedArrayView1D<UnsignedShort> indices = Containers::arrayCast<UnsignedShort>(indexData); |
||||||
|
|
||||||
|
Trade::MeshData mesh{MeshPrimitive::Points, |
||||||
|
std::move(indexData), Trade::MeshIndexData{indices}, 15}; |
||||||
|
|
||||||
|
Trade::MeshData filtered = filterOnlyAttributes(mesh, { |
||||||
|
Trade::MeshAttribute::Position |
||||||
|
}); |
||||||
|
CORRADE_COMPARE(filtered.primitive(), MeshPrimitive::Points); |
||||||
|
|
||||||
|
CORRADE_VERIFY(filtered.isIndexed()); |
||||||
|
CORRADE_COMPARE(filtered.indexCount(), 5); |
||||||
|
CORRADE_COMPARE(filtered.indexType(), MeshIndexType::UnsignedShort); |
||||||
|
CORRADE_COMPARE(filtered.indexData().data(), indices.data()); |
||||||
|
CORRADE_COMPARE(filtered.indexDataFlags(), Trade::DataFlags{}); |
||||||
|
|
||||||
|
/* The vertex count should get preserved even if there are no attributes */ |
||||||
|
CORRADE_COMPARE(filtered.vertexCount(), 15); |
||||||
|
/* Consistent with behavior in reference() for vertex-less meshes */ |
||||||
|
CORRADE_COMPARE(filtered.vertexDataFlags(), Trade::DataFlags{}); |
||||||
|
|
||||||
|
CORRADE_COMPARE(filtered.attributeCount(), 0); |
||||||
|
} |
||||||
|
|
||||||
|
void FilterAttributesTest::filterOnlyAttributeIds() { |
||||||
|
auto&& data = ImplementationSpecificIndexTypeData[testCaseInstanceId()]; |
||||||
|
setTestCaseDescription(data.name); |
||||||
|
|
||||||
|
Containers::Array<char> indexData{5*sizeof(UnsignedShort)}; |
||||||
|
Containers::StridedArrayView1D<UnsignedShort> indices = Containers::arrayCast<UnsignedShort>(indexData); |
||||||
|
Containers::Array<char> vertexData{3*sizeof(Vertex)}; |
||||||
|
Containers::StridedArrayView1D<Vertex> vertices = Containers::arrayCast<Vertex>(vertexData); |
||||||
|
|
||||||
|
Trade::MeshData mesh{MeshPrimitive::TriangleStrip, |
||||||
|
std::move(indexData), Trade::MeshIndexData{data.indexType, indices}, |
||||||
|
std::move(vertexData), { |
||||||
|
Trade::MeshAttributeData{Trade::MeshAttribute::Position, vertices.slice(&Vertex::position)}, |
||||||
|
Trade::MeshAttributeData{Trade::MeshAttribute::Tangent, vertices.slice(&Vertex::tangent)}, |
||||||
|
Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates, vertices.slice(&Vertex::textureCoordinates1)}, |
||||||
|
Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates, vertices.slice(&Vertex::textureCoordinates2)}, |
||||||
|
}}; |
||||||
|
|
||||||
|
Trade::MeshData filtered = filterOnlyAttributes(mesh, { |
||||||
|
/* The attribute 1 is specified twice, but that won't result in the
|
||||||
|
same attribute being added twice */ |
||||||
|
1, 1, 3 |
||||||
|
}); |
||||||
|
CORRADE_COMPARE(filtered.primitive(), MeshPrimitive::TriangleStrip); |
||||||
|
|
||||||
|
CORRADE_VERIFY(filtered.isIndexed()); |
||||||
|
CORRADE_COMPARE(filtered.indexCount(), 5); |
||||||
|
CORRADE_COMPARE(filtered.indexType(), data.indexType); |
||||||
|
CORRADE_COMPARE(filtered.indexData().data(), indices.data()); |
||||||
|
CORRADE_COMPARE(filtered.indexDataFlags(), Trade::DataFlags{}); |
||||||
|
|
||||||
|
CORRADE_COMPARE(filtered.vertexCount(), 3); |
||||||
|
CORRADE_COMPARE(filtered.vertexData().data(), vertices.data()); |
||||||
|
CORRADE_COMPARE(filtered.vertexDataFlags(), Trade::DataFlags{}); |
||||||
|
|
||||||
|
/* Testing just the offset if it matches expectations, the
|
||||||
|
MeshAttributeData is copied directly so no metadata should get lost */ |
||||||
|
CORRADE_COMPARE(filtered.attributeCount(), 2); |
||||||
|
CORRADE_COMPARE(filtered.attributeName(0), Trade::MeshAttribute::Tangent); |
||||||
|
CORRADE_COMPARE(filtered.attributeOffset(0), offsetof(Vertex, tangent)); |
||||||
|
CORRADE_COMPARE(filtered.attributeName(1), Trade::MeshAttribute::TextureCoordinates); |
||||||
|
CORRADE_COMPARE(filtered.attributeOffset(1), offsetof(Vertex, textureCoordinates2)); |
||||||
|
|
||||||
|
/* The attribute data should not be a growable array to make this usable in
|
||||||
|
plugins */ |
||||||
|
Containers::Array<Trade::MeshAttributeData> attributeData = filtered.releaseAttributeData(); |
||||||
|
CORRADE_VERIFY(!attributeData.deleter()); |
||||||
|
} |
||||||
|
|
||||||
|
void FilterAttributesTest::filterOnlyAttributeIdsOutOfBounds() { |
||||||
|
#ifdef CORRADE_NO_ASSERT |
||||||
|
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); |
||||||
|
#endif |
||||||
|
|
||||||
|
Vertex vertices[3]{}; |
||||||
|
|
||||||
|
Trade::MeshData mesh{MeshPrimitive::TriangleFan, |
||||||
|
{}, vertices, { |
||||||
|
Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::stridedArrayView(vertices).slice(&Vertex::position)}, |
||||||
|
Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates, Containers::stridedArrayView(vertices).slice(&Vertex::textureCoordinates1)} |
||||||
|
}}; |
||||||
|
|
||||||
|
std::ostringstream out; |
||||||
|
Error redirectError{&out}; |
||||||
|
filterOnlyAttributes(mesh, {0, 0, 2}); |
||||||
|
CORRADE_COMPARE(out.str(), "MeshTools::filterOnlyAttributes(): index 2 out of range for 2 attributes\n"); |
||||||
|
} |
||||||
|
|
||||||
|
void FilterAttributesTest::filterOnlyAttributeIdsNoIndexData() { |
||||||
|
/* A trivial subset of filterOnlyAttributeIds() testing it doesn't blow up
|
||||||
|
if the mesh is not indexed */ |
||||||
|
|
||||||
|
Containers::Array<char> vertexData{3*sizeof(Vertex)}; |
||||||
|
Containers::StridedArrayView1D<Vertex> vertices = Containers::arrayCast<Vertex>(vertexData); |
||||||
|
|
||||||
|
Trade::MeshData mesh{MeshPrimitive::TriangleFan, |
||||||
|
std::move(vertexData), { |
||||||
|
Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates, vertices.slice(&Vertex::textureCoordinates1)} |
||||||
|
}}; |
||||||
|
|
||||||
|
Trade::MeshData filtered = filterOnlyAttributes(mesh, { |
||||||
|
0 |
||||||
|
}); |
||||||
|
CORRADE_COMPARE(filtered.primitive(), MeshPrimitive::TriangleFan); |
||||||
|
|
||||||
|
CORRADE_VERIFY(!filtered.isIndexed()); |
||||||
|
/* Consistent with behavior in reference() for index-less meshes */ |
||||||
|
CORRADE_COMPARE(filtered.indexDataFlags(), Trade::DataFlags{}); |
||||||
|
|
||||||
|
CORRADE_COMPARE(filtered.vertexCount(), 3); |
||||||
|
CORRADE_COMPARE(filtered.vertexData().data(), vertices.data()); |
||||||
|
CORRADE_COMPARE(filtered.vertexDataFlags(), Trade::DataFlags{}); |
||||||
|
|
||||||
|
/* Testing just the offset if it matches expectations, the
|
||||||
|
MeshAttributeData is copied directly so no metadata should get lost */ |
||||||
|
CORRADE_COMPARE(filtered.attributeCount(), 1); |
||||||
|
CORRADE_COMPARE(filtered.attributeName(0), Trade::MeshAttribute::TextureCoordinates); |
||||||
|
CORRADE_COMPARE(filtered.attributeOffset(0), offsetof(Vertex, textureCoordinates1)); |
||||||
|
} |
||||||
|
|
||||||
|
void FilterAttributesTest::filterOnlyAttributeIdsNoAttributeData() { |
||||||
|
Containers::Array<char> indexData{5*sizeof(UnsignedShort)}; |
||||||
|
Containers::StridedArrayView1D<UnsignedShort> indices = Containers::arrayCast<UnsignedShort>(indexData); |
||||||
|
|
||||||
|
Trade::MeshData mesh{MeshPrimitive::Points, |
||||||
|
std::move(indexData), Trade::MeshIndexData{indices}, 15}; |
||||||
|
|
||||||
|
Trade::MeshData filtered = filterOnlyAttributes(mesh, std::initializer_list<UnsignedInt>{}); |
||||||
|
CORRADE_COMPARE(filtered.primitive(), MeshPrimitive::Points); |
||||||
|
|
||||||
|
CORRADE_VERIFY(filtered.isIndexed()); |
||||||
|
CORRADE_COMPARE(filtered.indexCount(), 5); |
||||||
|
CORRADE_COMPARE(filtered.indexType(), MeshIndexType::UnsignedShort); |
||||||
|
CORRADE_COMPARE(filtered.indexData().data(), indices.data()); |
||||||
|
CORRADE_COMPARE(filtered.indexDataFlags(), Trade::DataFlags{}); |
||||||
|
|
||||||
|
/* The vertex count should get preserved even if there are no attributes */ |
||||||
|
CORRADE_COMPARE(filtered.vertexCount(), 15); |
||||||
|
/* Consistent with behavior in reference() for vertex-less meshes */ |
||||||
|
CORRADE_COMPARE(filtered.vertexDataFlags(), Trade::DataFlags{}); |
||||||
|
|
||||||
|
CORRADE_COMPARE(filtered.attributeCount(), 0); |
||||||
|
} |
||||||
|
|
||||||
|
void FilterAttributesTest::filterExceptAttributeNames() { |
||||||
|
auto&& data = ImplementationSpecificIndexTypeData[testCaseInstanceId()]; |
||||||
|
setTestCaseDescription(data.name); |
||||||
|
|
||||||
|
Containers::Array<char> indexData{5*sizeof(UnsignedShort)}; |
||||||
|
Containers::StridedArrayView1D<UnsignedShort> indices = Containers::arrayCast<UnsignedShort>(indexData); |
||||||
|
Containers::Array<char> vertexData{3*sizeof(Vertex)}; |
||||||
|
Containers::StridedArrayView1D<Vertex> vertices = Containers::arrayCast<Vertex>(vertexData); |
||||||
|
|
||||||
|
Trade::MeshData mesh{MeshPrimitive::TriangleStrip, |
||||||
|
std::move(indexData), Trade::MeshIndexData{data.indexType, indices}, |
||||||
|
std::move(vertexData), { |
||||||
|
Trade::MeshAttributeData{Trade::MeshAttribute::Position, vertices.slice(&Vertex::position)}, |
||||||
|
Trade::MeshAttributeData{Trade::MeshAttribute::Tangent, vertices.slice(&Vertex::tangent)}, |
||||||
|
Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates, vertices.slice(&Vertex::textureCoordinates1)}, |
||||||
|
Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates, vertices.slice(&Vertex::textureCoordinates2)}, |
||||||
|
/* Positions again, just under a different name. Should be kept. */ |
||||||
|
Trade::MeshAttributeData{Trade::meshAttributeCustom(0xbaf), vertices.slice(&Vertex::position)}, |
||||||
|
}}; |
||||||
|
|
||||||
|
Trade::MeshData filtered = filterExceptAttributes(mesh, { |
||||||
|
Trade::MeshAttribute::Position, |
||||||
|
Trade::MeshAttribute::Normal, /* not present, ignored */ |
||||||
|
Trade::MeshAttribute::TextureCoordinates, /* present twice */ |
||||||
|
}); |
||||||
|
CORRADE_COMPARE(filtered.primitive(), MeshPrimitive::TriangleStrip); |
||||||
|
|
||||||
|
CORRADE_VERIFY(filtered.isIndexed()); |
||||||
|
CORRADE_COMPARE(filtered.indexCount(), 5); |
||||||
|
CORRADE_COMPARE(filtered.indexType(), data.indexType); |
||||||
|
CORRADE_COMPARE(filtered.indexData().data(), indices.data()); |
||||||
|
CORRADE_COMPARE(filtered.indexDataFlags(), Trade::DataFlags{}); |
||||||
|
|
||||||
|
CORRADE_COMPARE(filtered.vertexCount(), 3); |
||||||
|
CORRADE_COMPARE(filtered.vertexData().data(), vertices.data()); |
||||||
|
CORRADE_COMPARE(filtered.vertexDataFlags(), Trade::DataFlags{}); |
||||||
|
|
||||||
|
/* Testing just the offset if it matches expectations, the
|
||||||
|
MeshAttributeData is copied directly so no metadata should get lost */ |
||||||
|
CORRADE_COMPARE(filtered.attributeCount(), 2); |
||||||
|
CORRADE_COMPARE(filtered.attributeName(0), Trade::MeshAttribute::Tangent); |
||||||
|
CORRADE_COMPARE(filtered.attributeOffset(0), offsetof(Vertex, tangent)); |
||||||
|
CORRADE_COMPARE(filtered.attributeName(1), Trade::meshAttributeCustom(0xbaf)); |
||||||
|
CORRADE_COMPARE(filtered.attributeOffset(1), offsetof(Vertex, position)); |
||||||
|
|
||||||
|
/* The attribute data should not be a growable array to make this usable in
|
||||||
|
plugins */ |
||||||
|
Containers::Array<Trade::MeshAttributeData> attributeData = filtered.releaseAttributeData(); |
||||||
|
CORRADE_VERIFY(!attributeData.deleter()); |
||||||
|
} |
||||||
|
|
||||||
|
void FilterAttributesTest::filterExceptAttributeNamesNoIndexData() { |
||||||
|
/* A trivial subset of filterExceptAttributeNames() testing it doesn't blow
|
||||||
|
up if the mesh is not indexed */ |
||||||
|
|
||||||
|
Containers::Array<char> vertexData{3*sizeof(Vertex)}; |
||||||
|
Containers::StridedArrayView1D<Vertex> vertices = Containers::arrayCast<Vertex>(vertexData); |
||||||
|
|
||||||
|
Trade::MeshData mesh{MeshPrimitive::TriangleFan, |
||||||
|
std::move(vertexData), { |
||||||
|
Trade::MeshAttributeData{Trade::MeshAttribute::Position, vertices.slice(&Vertex::position)}, |
||||||
|
Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates, vertices.slice(&Vertex::textureCoordinates1)} |
||||||
|
}}; |
||||||
|
|
||||||
|
Trade::MeshData filtered = filterExceptAttributes(mesh, { |
||||||
|
Trade::MeshAttribute::Position |
||||||
|
}); |
||||||
|
CORRADE_COMPARE(filtered.primitive(), MeshPrimitive::TriangleFan); |
||||||
|
|
||||||
|
CORRADE_VERIFY(!filtered.isIndexed()); |
||||||
|
/* Consistent with behavior in reference() for index-less meshes */ |
||||||
|
CORRADE_COMPARE(filtered.indexDataFlags(), Trade::DataFlags{}); |
||||||
|
|
||||||
|
CORRADE_COMPARE(filtered.vertexCount(), 3); |
||||||
|
CORRADE_COMPARE(filtered.vertexData().data(), vertices.data()); |
||||||
|
CORRADE_COMPARE(filtered.vertexDataFlags(), Trade::DataFlags{}); |
||||||
|
|
||||||
|
/* Testing just the offset if it matches expectations, the
|
||||||
|
MeshAttributeData is copied directly so no metadata should get lost */ |
||||||
|
CORRADE_COMPARE(filtered.attributeCount(), 1); |
||||||
|
CORRADE_COMPARE(filtered.attributeName(0), Trade::MeshAttribute::TextureCoordinates); |
||||||
|
CORRADE_COMPARE(filtered.attributeOffset(0), offsetof(Vertex, textureCoordinates1)); |
||||||
|
} |
||||||
|
|
||||||
|
void FilterAttributesTest::filterExceptAttributeNamesNoAttributeData() { |
||||||
|
Containers::Array<char> indexData{5*sizeof(UnsignedShort)}; |
||||||
|
Containers::StridedArrayView1D<UnsignedShort> indices = Containers::arrayCast<UnsignedShort>(indexData); |
||||||
|
|
||||||
|
Trade::MeshData mesh{MeshPrimitive::Points, |
||||||
|
std::move(indexData), Trade::MeshIndexData{indices}, 15}; |
||||||
|
|
||||||
|
Trade::MeshData filtered = filterExceptAttributes(mesh, { |
||||||
|
Trade::MeshAttribute::Position |
||||||
|
}); |
||||||
|
CORRADE_COMPARE(filtered.primitive(), MeshPrimitive::Points); |
||||||
|
|
||||||
|
CORRADE_VERIFY(filtered.isIndexed()); |
||||||
|
CORRADE_COMPARE(filtered.indexCount(), 5); |
||||||
|
CORRADE_COMPARE(filtered.indexType(), MeshIndexType::UnsignedShort); |
||||||
|
CORRADE_COMPARE(filtered.indexData().data(), indices.data()); |
||||||
|
CORRADE_COMPARE(filtered.indexDataFlags(), Trade::DataFlags{}); |
||||||
|
|
||||||
|
/* The vertex count should get preserved even if there are no attributes */ |
||||||
|
CORRADE_COMPARE(filtered.vertexCount(), 15); |
||||||
|
/* Consistent with behavior in reference() for vertex-less meshes */ |
||||||
|
CORRADE_COMPARE(filtered.vertexDataFlags(), Trade::DataFlags{}); |
||||||
|
|
||||||
|
CORRADE_COMPARE(filtered.attributeCount(), 0); |
||||||
|
} |
||||||
|
|
||||||
|
void FilterAttributesTest::filterExceptAttributeIds() { |
||||||
|
auto&& data = ImplementationSpecificIndexTypeData[testCaseInstanceId()]; |
||||||
|
setTestCaseDescription(data.name); |
||||||
|
|
||||||
|
Containers::Array<char> indexData{5*sizeof(UnsignedShort)}; |
||||||
|
Containers::StridedArrayView1D<UnsignedShort> indices = Containers::arrayCast<UnsignedShort>(indexData); |
||||||
|
Containers::Array<char> vertexData{3*sizeof(Vertex)}; |
||||||
|
Containers::StridedArrayView1D<Vertex> vertices = Containers::arrayCast<Vertex>(vertexData); |
||||||
|
|
||||||
|
Trade::MeshData mesh{MeshPrimitive::TriangleStrip, |
||||||
|
std::move(indexData), Trade::MeshIndexData{data.indexType, indices}, |
||||||
|
std::move(vertexData), { |
||||||
|
Trade::MeshAttributeData{Trade::MeshAttribute::Position, vertices.slice(&Vertex::position)}, |
||||||
|
Trade::MeshAttributeData{Trade::MeshAttribute::Tangent, vertices.slice(&Vertex::tangent)}, |
||||||
|
Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates, vertices.slice(&Vertex::textureCoordinates1)}, |
||||||
|
Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates, vertices.slice(&Vertex::textureCoordinates2)}, |
||||||
|
}}; |
||||||
|
|
||||||
|
Trade::MeshData filtered = filterExceptAttributes(mesh, { |
||||||
|
/* The attribute 1 is specified twice, but that won't result in
|
||||||
|
attribute 1 being removed and then again */ |
||||||
|
1, 1, 3 |
||||||
|
}); |
||||||
|
CORRADE_COMPARE(filtered.primitive(), MeshPrimitive::TriangleStrip); |
||||||
|
|
||||||
|
CORRADE_VERIFY(filtered.isIndexed()); |
||||||
|
CORRADE_COMPARE(filtered.indexCount(), 5); |
||||||
|
CORRADE_COMPARE(filtered.indexType(), data.indexType); |
||||||
|
CORRADE_COMPARE(filtered.indexData().data(), indices.data()); |
||||||
|
CORRADE_COMPARE(filtered.indexDataFlags(), Trade::DataFlags{}); |
||||||
|
|
||||||
|
CORRADE_COMPARE(filtered.vertexCount(), 3); |
||||||
|
CORRADE_COMPARE(filtered.vertexData().data(), vertices.data()); |
||||||
|
CORRADE_COMPARE(filtered.vertexDataFlags(), Trade::DataFlags{}); |
||||||
|
|
||||||
|
/* Testing just the offset if it matches expectations, the
|
||||||
|
MeshAttributeData is copied directly so no metadata should get lost */ |
||||||
|
CORRADE_COMPARE(filtered.attributeCount(), 2); |
||||||
|
CORRADE_COMPARE(filtered.attributeName(0), Trade::MeshAttribute::Position); |
||||||
|
CORRADE_COMPARE(filtered.attributeOffset(0), offsetof(Vertex, position)); |
||||||
|
CORRADE_COMPARE(filtered.attributeName(1), Trade::MeshAttribute::TextureCoordinates); |
||||||
|
CORRADE_COMPARE(filtered.attributeOffset(1), offsetof(Vertex, textureCoordinates1)); |
||||||
|
|
||||||
|
/* The attribute data should not be a growable array to make this usable in
|
||||||
|
plugins */ |
||||||
|
Containers::Array<Trade::MeshAttributeData> attributeData = filtered.releaseAttributeData(); |
||||||
|
CORRADE_VERIFY(!attributeData.deleter()); |
||||||
|
} |
||||||
|
|
||||||
|
void FilterAttributesTest::filterExceptAttributeIdsOutOfBounds() { |
||||||
|
#ifdef CORRADE_NO_ASSERT |
||||||
|
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions"); |
||||||
|
#endif |
||||||
|
|
||||||
|
Vertex vertices[3]{}; |
||||||
|
|
||||||
|
Trade::MeshData mesh{MeshPrimitive::TriangleFan, |
||||||
|
{}, vertices, { |
||||||
|
Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::stridedArrayView(vertices).slice(&Vertex::position)}, |
||||||
|
Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates, Containers::stridedArrayView(vertices).slice(&Vertex::textureCoordinates1)} |
||||||
|
}}; |
||||||
|
|
||||||
|
std::ostringstream out; |
||||||
|
Error redirectError{&out}; |
||||||
|
filterExceptAttributes(mesh, {0, 0, 2}); |
||||||
|
CORRADE_COMPARE(out.str(), "MeshTools::filterExceptAttributes(): index 2 out of range for 2 attributes\n"); |
||||||
|
} |
||||||
|
|
||||||
|
void FilterAttributesTest::filterExceptAttributeIdsNoIndexData() { |
||||||
|
/* A trivial subset of filterExceptAttributeIds() testing it doesn't blow up
|
||||||
|
if the mesh is not indexed */ |
||||||
|
|
||||||
|
Containers::Array<char> vertexData{3*sizeof(Vertex)}; |
||||||
|
Containers::StridedArrayView1D<Vertex> vertices = Containers::arrayCast<Vertex>(vertexData); |
||||||
|
|
||||||
|
Trade::MeshData mesh{MeshPrimitive::TriangleFan, |
||||||
|
std::move(vertexData), { |
||||||
|
Trade::MeshAttributeData{Trade::MeshAttribute::Position, vertices.slice(&Vertex::position)}, |
||||||
|
Trade::MeshAttributeData{Trade::MeshAttribute::TextureCoordinates, vertices.slice(&Vertex::textureCoordinates1)} |
||||||
|
}}; |
||||||
|
|
||||||
|
Trade::MeshData filtered = filterExceptAttributes(mesh, { |
||||||
|
0 |
||||||
|
}); |
||||||
|
CORRADE_COMPARE(filtered.primitive(), MeshPrimitive::TriangleFan); |
||||||
|
|
||||||
|
CORRADE_VERIFY(!filtered.isIndexed()); |
||||||
|
/* Consistent with behavior in reference() for index-less meshes */ |
||||||
|
CORRADE_COMPARE(filtered.indexDataFlags(), Trade::DataFlags{}); |
||||||
|
|
||||||
|
CORRADE_COMPARE(filtered.vertexCount(), 3); |
||||||
|
CORRADE_COMPARE(filtered.vertexData().data(), vertices.data()); |
||||||
|
CORRADE_COMPARE(filtered.vertexDataFlags(), Trade::DataFlags{}); |
||||||
|
|
||||||
|
/* Testing just the offset if it matches expectations, the
|
||||||
|
MeshAttributeData is copied directly so no metadata should get lost */ |
||||||
|
CORRADE_COMPARE(filtered.attributeCount(), 1); |
||||||
|
CORRADE_COMPARE(filtered.attributeName(0), Trade::MeshAttribute::TextureCoordinates); |
||||||
|
CORRADE_COMPARE(filtered.attributeOffset(0), offsetof(Vertex, textureCoordinates1)); |
||||||
|
} |
||||||
|
|
||||||
|
void FilterAttributesTest::filterExceptAttributeIdsNoAttributeData() { |
||||||
|
Containers::Array<char> indexData{5*sizeof(UnsignedShort)}; |
||||||
|
Containers::StridedArrayView1D<UnsignedShort> indices = Containers::arrayCast<UnsignedShort>(indexData); |
||||||
|
|
||||||
|
Trade::MeshData mesh{MeshPrimitive::Points, |
||||||
|
std::move(indexData), Trade::MeshIndexData{indices}, 15}; |
||||||
|
|
||||||
|
Trade::MeshData filtered = filterExceptAttributes(mesh, std::initializer_list<UnsignedInt>{}); |
||||||
|
CORRADE_COMPARE(filtered.primitive(), MeshPrimitive::Points); |
||||||
|
|
||||||
|
CORRADE_VERIFY(filtered.isIndexed()); |
||||||
|
CORRADE_COMPARE(filtered.indexCount(), 5); |
||||||
|
CORRADE_COMPARE(filtered.indexType(), MeshIndexType::UnsignedShort); |
||||||
|
CORRADE_COMPARE(filtered.indexData().data(), indices.data()); |
||||||
|
CORRADE_COMPARE(filtered.indexDataFlags(), Trade::DataFlags{}); |
||||||
|
|
||||||
|
/* The vertex count should get preserved even if there are no attributes */ |
||||||
|
CORRADE_COMPARE(filtered.vertexCount(), 15); |
||||||
|
/* Consistent with behavior in reference() for vertex-less meshes */ |
||||||
|
CORRADE_COMPARE(filtered.vertexDataFlags(), Trade::DataFlags{}); |
||||||
|
|
||||||
|
CORRADE_COMPARE(filtered.attributeCount(), 0); |
||||||
|
} |
||||||
|
|
||||||
|
}}}} |
||||||
|
|
||||||
|
CORRADE_TEST_MAIN(Magnum::MeshTools::Test::FilterAttributesTest) |
||||||
Loading…
Reference in new issue