/* This file is part of Magnum. Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Vladimír Vondruš Copyright © 2020 Jonathan Hale 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 #include #include #include #include #include #include "Magnum/Math/Color.h" #include "Magnum/Math/Half.h" #include "Magnum/Trade/MeshData.h" namespace Magnum { namespace Trade { namespace Test { namespace { struct MeshDataTest: TestSuite::Tester { explicit MeshDataTest(); void customAttributeName(); void customAttributeNameTooLarge(); void customAttributeNameNotCustom(); void debugAttributeName(); void debugAttributeNamePacked(); void constructIndexContiguous(); void constructIndexStrided(); void constructIndexStridedWrongStride(); void constructIndexTypeErasedContiguous(); void constructIndexTypeErasedContiguousImplementationSpecificFormat(); void constructIndexTypeErasedContiguousWrongSize(); void constructIndexTypeErasedStrided(); void constructIndexTypeErasedStridedImplementationSpecificFormat(); void constructIndexTypeErasedStridedWrongStride(); void constructIndex2D(); void constructIndex2DNotIndexed(); void constructIndex2DWrongSize(); void constructIndex2DWrongStride(); void constructIndex2DNonContiguous(); void constructIndexNullptr(); void constructAttribute(); void constructAttributeDefault(); void constructAttributeCustom(); void constructAttribute2D(); void constructAttribute2DWrongSize(); void constructAttribute2DNonContiguous(); void constructAttributeTypeErased(); void constructAttributeNullptr(); void constructAttributePadding(); void constructAttributeNonOwningArray(); void constructAttributeOffsetOnly(); void constructAttributeImplementationSpecificFormat(); void constructAttributeWrongFormat(); void constructAttributeWrongStride(); void constructAttributeWrongDataAccess(); void constructAttributeOnlyArrayAllowed(); void constructArrayAttribute(); void constructArrayAttributeNonContiguous(); void constructArrayAttribute2D(); void constructArrayAttribute2DWrongSize(); void constructArrayAttribute2DNonContiguous(); void constructArrayAttributeTypeErased(); void constructArrayAttributeNullptr(); void constructArrayAttributeOffsetOnly(); void constructArrayAttributeImplementationSpecificFormat(); void constructArrayAttributeNotAllowed(); void construct(); void constructZeroIndices(); void constructZeroAttributes(); void constructZeroVertices(); void constructIndexless(); void constructIndexlessZeroVertices(); void constructAttributeless(); void constructIndexlessAttributeless(); void constructIndexlessAttributelessZeroVertices(); void constructImplementationSpecificIndexType(); void constructImplementationSpecificVertexFormat(); void constructSpecialIndexStrides(); void constructSpecialIndexStridesImplementationSpecificIndexType(); void constructSpecialAttributeStrides(); void constructSpecialAttributeStridesImplementationSpecificVertexFormat(); void constructNotOwned(); void constructIndicesNotOwned(); void constructVerticesNotOwned(); void constructIndexlessNotOwned(); void constructAttributelessNotOwned(); void constructIndexDataButNotIndexed(); void constructAttributelessImplicitVertexCount(); void constructIndicesNotContained(); void constructAttributeNotContained(); void constructInconsitentVertexCount(); void constructDifferentJointIdWeightCount(); void constructInconsistentJointIdWeightArraySizes(); void constructNotOwnedIndexFlagOwned(); void constructNotOwnedVertexFlagOwned(); void constructIndicesNotOwnedFlagOwned(); void constructVerticesNotOwnedFlagOwned(); void constructIndexlessNotOwnedFlagOwned(); void constructAttributelessNotOwnedFlagOwned(); void constructInvalidAttributeData(); void constructCopy(); void constructMove(); template void indicesAsArray(); void indicesIntoArrayInvalidSize(); template void positions2DAsArray(); template void positions2DAsArrayPackedUnsigned(); template void positions2DAsArrayPackedSigned(); template void positions2DAsArrayPackedUnsignedNormalized(); template void positions2DAsArrayPackedSignedNormalized(); void positions2DIntoArrayInvalidSize(); template void positions3DAsArray(); template void positions3DAsArrayPackedUnsigned(); template void positions3DAsArrayPackedSigned(); template void positions3DAsArrayPackedUnsignedNormalized(); template void positions3DAsArrayPackedSignedNormalized(); void positions3DIntoArrayInvalidSize(); template void tangentsAsArray(); template void tangentsAsArrayPackedSignedNormalized(); void tangentsIntoArrayInvalidSize(); template void bitangentSignsAsArray(); template void bitangentSignsAsArrayPackedSignedNormalized(); void bitangentSignsAsArrayNotFourComponent(); void bitangentSignsIntoArrayInvalidSize(); template void bitangentsAsArray(); template void bitangentsAsArrayPackedSignedNormalized(); void bitangentsIntoArrayInvalidSize(); template void normalsAsArray(); template void normalsAsArrayPackedSignedNormalized(); void normalsIntoArrayInvalidSize(); template void textureCoordinates2DAsArray(); template void textureCoordinates2DAsArrayPackedUnsigned(); template void textureCoordinates2DAsArrayPackedSigned(); template void textureCoordinates2DAsArrayPackedUnsignedNormalized(); template void textureCoordinates2DAsArrayPackedSignedNormalized(); void textureCoordinates2DIntoArrayInvalidSize(); template void colorsAsArray(); template void colorsAsArrayPackedUnsignedNormalized(); void colorsIntoArrayInvalidSize(); template void jointIdsAsArray(); void jointIdsIntoArrayInvalidSizeStride(); template void weightsAsArray(); template void weightsAsArrayPackedUnsignedNormalized(); void weightsIntoArrayInvalidSizeStride(); template void objectIdsAsArray(); void objectIdsIntoArrayInvalidSize(); void implementationSpecificIndexTypeWrongAccess(); void implementationSpecificVertexFormatWrongAccess(); void mutableAccessNotAllowed(); void indicesNotIndexed(); void indicesWrongType(); void attributeNotFound(); void attributeWrongType(); void attributeWrongArrayAccess(); void releaseIndexData(); void releaseAttributeData(); void releaseVertexData(); }; const struct { const char* name; UnsignedInt vertexCount, expectedVertexCount; } ConstructData[] { {"implicit vertex count", MeshData::ImplicitVertexCount, 3}, {"explicit vertex count", 3, 3}, {"explicit large vertex count", 17, 17}, {"explicit zero vertex count", 0, 0} }; const struct { const char* name; DataFlags indexDataFlags, vertexDataFlags; } NotOwnedData[] { {"", {}, {}}, {"indices mutable", DataFlag::Mutable, {}}, {"vertices mutable", {}, DataFlag::Mutable}, {"both mutable", DataFlag::Mutable, DataFlag::Mutable} }; const struct { const char* name; DataFlags dataFlags; } SingleNotOwnedData[] { {"", {}}, {"mutable", DataFlag::Mutable} }; MeshDataTest::MeshDataTest() { addTests({&MeshDataTest::customAttributeName, &MeshDataTest::customAttributeNameTooLarge, &MeshDataTest::customAttributeNameNotCustom, &MeshDataTest::debugAttributeName, &MeshDataTest::debugAttributeNamePacked, &MeshDataTest::constructIndexContiguous, &MeshDataTest::constructIndexStrided, &MeshDataTest::constructIndexStridedWrongStride, &MeshDataTest::constructIndexTypeErasedContiguous, &MeshDataTest::constructIndexTypeErasedContiguousImplementationSpecificFormat, &MeshDataTest::constructIndexTypeErasedContiguousWrongSize, &MeshDataTest::constructIndexTypeErasedStrided, &MeshDataTest::constructIndexTypeErasedStridedImplementationSpecificFormat, &MeshDataTest::constructIndexTypeErasedStridedWrongStride, &MeshDataTest::constructIndex2D, &MeshDataTest::constructIndex2DNotIndexed, &MeshDataTest::constructIndex2DWrongSize, &MeshDataTest::constructIndex2DWrongStride, &MeshDataTest::constructIndex2DNonContiguous, &MeshDataTest::constructIndexNullptr, &MeshDataTest::constructAttribute, &MeshDataTest::constructAttributeDefault, &MeshDataTest::constructAttributeCustom, &MeshDataTest::constructAttribute2D, &MeshDataTest::constructAttribute2DWrongSize, &MeshDataTest::constructAttribute2DNonContiguous, &MeshDataTest::constructAttributeTypeErased, &MeshDataTest::constructAttributeNullptr, &MeshDataTest::constructAttributePadding, &MeshDataTest::constructAttributeNonOwningArray, &MeshDataTest::constructAttributeOffsetOnly, &MeshDataTest::constructAttributeImplementationSpecificFormat, &MeshDataTest::constructAttributeWrongFormat, &MeshDataTest::constructAttributeWrongStride, &MeshDataTest::constructAttributeWrongDataAccess, &MeshDataTest::constructAttributeOnlyArrayAllowed, &MeshDataTest::constructArrayAttribute, &MeshDataTest::constructArrayAttributeNonContiguous, &MeshDataTest::constructArrayAttribute2D, &MeshDataTest::constructArrayAttribute2DWrongSize, &MeshDataTest::constructArrayAttribute2DNonContiguous, &MeshDataTest::constructArrayAttributeTypeErased, &MeshDataTest::constructArrayAttributeNullptr, &MeshDataTest::constructArrayAttributeOffsetOnly, &MeshDataTest::constructArrayAttributeImplementationSpecificFormat, &MeshDataTest::constructArrayAttributeNotAllowed}); addInstancedTests({&MeshDataTest::construct}, Containers::arraySize(ConstructData)); addTests({&MeshDataTest::constructZeroIndices, &MeshDataTest::constructZeroAttributes, &MeshDataTest::constructZeroVertices, &MeshDataTest::constructIndexless, &MeshDataTest::constructIndexlessZeroVertices, &MeshDataTest::constructAttributeless, &MeshDataTest::constructIndexlessAttributeless, &MeshDataTest::constructIndexlessAttributelessZeroVertices, &MeshDataTest::constructImplementationSpecificIndexType, &MeshDataTest::constructImplementationSpecificVertexFormat, &MeshDataTest::constructSpecialIndexStrides, &MeshDataTest::constructSpecialIndexStridesImplementationSpecificIndexType, &MeshDataTest::constructSpecialAttributeStrides, &MeshDataTest::constructSpecialAttributeStridesImplementationSpecificVertexFormat}); addInstancedTests({&MeshDataTest::constructNotOwned}, Containers::arraySize(NotOwnedData)); addInstancedTests({&MeshDataTest::constructIndicesNotOwned, &MeshDataTest::constructVerticesNotOwned, &MeshDataTest::constructIndexlessNotOwned, &MeshDataTest::constructAttributelessNotOwned}, Containers::arraySize(SingleNotOwnedData)); addTests({&MeshDataTest::constructIndexDataButNotIndexed, &MeshDataTest::constructAttributelessImplicitVertexCount, &MeshDataTest::constructIndicesNotContained, &MeshDataTest::constructAttributeNotContained, &MeshDataTest::constructInconsitentVertexCount, &MeshDataTest::constructDifferentJointIdWeightCount, &MeshDataTest::constructInconsistentJointIdWeightArraySizes, &MeshDataTest::constructNotOwnedIndexFlagOwned, &MeshDataTest::constructNotOwnedVertexFlagOwned, &MeshDataTest::constructIndicesNotOwnedFlagOwned, &MeshDataTest::constructVerticesNotOwnedFlagOwned, &MeshDataTest::constructIndexlessNotOwnedFlagOwned, &MeshDataTest::constructAttributelessNotOwnedFlagOwned, &MeshDataTest::constructInvalidAttributeData, &MeshDataTest::constructCopy, &MeshDataTest::constructMove, &MeshDataTest::indicesAsArray, &MeshDataTest::indicesAsArray, &MeshDataTest::indicesAsArray, &MeshDataTest::indicesIntoArrayInvalidSize, &MeshDataTest::positions2DAsArray, &MeshDataTest::positions2DAsArray, &MeshDataTest::positions2DAsArray, &MeshDataTest::positions2DAsArray, &MeshDataTest::positions2DAsArrayPackedUnsigned, &MeshDataTest::positions2DAsArrayPackedUnsigned, &MeshDataTest::positions2DAsArrayPackedUnsigned, &MeshDataTest::positions2DAsArrayPackedUnsigned, &MeshDataTest::positions2DAsArrayPackedSigned, &MeshDataTest::positions2DAsArrayPackedSigned, &MeshDataTest::positions2DAsArrayPackedSigned, &MeshDataTest::positions2DAsArrayPackedSigned, &MeshDataTest::positions2DAsArrayPackedUnsignedNormalized, &MeshDataTest::positions2DAsArrayPackedUnsignedNormalized, &MeshDataTest::positions2DAsArrayPackedUnsignedNormalized, &MeshDataTest::positions2DAsArrayPackedUnsignedNormalized, &MeshDataTest::positions2DAsArrayPackedSignedNormalized, &MeshDataTest::positions2DAsArrayPackedSignedNormalized, &MeshDataTest::positions2DAsArrayPackedSignedNormalized, &MeshDataTest::positions2DAsArrayPackedSignedNormalized, &MeshDataTest::positions2DIntoArrayInvalidSize, &MeshDataTest::positions3DAsArray, &MeshDataTest::positions3DAsArray, &MeshDataTest::positions3DAsArray, &MeshDataTest::positions3DAsArray, &MeshDataTest::positions3DAsArrayPackedUnsigned, &MeshDataTest::positions3DAsArrayPackedUnsigned, &MeshDataTest::positions3DAsArrayPackedUnsigned, &MeshDataTest::positions3DAsArrayPackedUnsigned, &MeshDataTest::positions3DAsArrayPackedSigned, &MeshDataTest::positions3DAsArrayPackedSigned, &MeshDataTest::positions3DAsArrayPackedSigned, &MeshDataTest::positions3DAsArrayPackedSigned, &MeshDataTest::positions3DAsArrayPackedUnsignedNormalized, &MeshDataTest::positions3DAsArrayPackedUnsignedNormalized, &MeshDataTest::positions3DAsArrayPackedUnsignedNormalized, &MeshDataTest::positions3DAsArrayPackedUnsignedNormalized, &MeshDataTest::positions3DAsArrayPackedSignedNormalized, &MeshDataTest::positions3DAsArrayPackedSignedNormalized, &MeshDataTest::positions3DAsArrayPackedSignedNormalized, &MeshDataTest::positions3DAsArrayPackedSignedNormalized, &MeshDataTest::positions3DIntoArrayInvalidSize, &MeshDataTest::tangentsAsArray, &MeshDataTest::tangentsAsArray, &MeshDataTest::tangentsAsArray, &MeshDataTest::tangentsAsArray, &MeshDataTest::tangentsAsArrayPackedSignedNormalized, &MeshDataTest::tangentsAsArrayPackedSignedNormalized, &MeshDataTest::tangentsAsArrayPackedSignedNormalized, &MeshDataTest::tangentsAsArrayPackedSignedNormalized, &MeshDataTest::tangentsIntoArrayInvalidSize, &MeshDataTest::bitangentSignsAsArray, &MeshDataTest::bitangentSignsAsArray, &MeshDataTest::bitangentSignsAsArrayPackedSignedNormalized, &MeshDataTest::bitangentSignsAsArrayPackedSignedNormalized, &MeshDataTest::bitangentSignsAsArrayNotFourComponent, &MeshDataTest::bitangentSignsIntoArrayInvalidSize, &MeshDataTest::bitangentsAsArray, &MeshDataTest::bitangentsAsArray, &MeshDataTest::bitangentsAsArrayPackedSignedNormalized, &MeshDataTest::bitangentsAsArrayPackedSignedNormalized, &MeshDataTest::bitangentsIntoArrayInvalidSize, &MeshDataTest::normalsAsArray, &MeshDataTest::normalsAsArray, &MeshDataTest::normalsAsArrayPackedSignedNormalized, &MeshDataTest::normalsAsArrayPackedSignedNormalized, &MeshDataTest::normalsIntoArrayInvalidSize, &MeshDataTest::textureCoordinates2DAsArray, &MeshDataTest::textureCoordinates2DAsArray, &MeshDataTest::textureCoordinates2DAsArrayPackedUnsigned, &MeshDataTest::textureCoordinates2DAsArrayPackedUnsigned, &MeshDataTest::textureCoordinates2DAsArrayPackedSigned, &MeshDataTest::textureCoordinates2DAsArrayPackedSigned, &MeshDataTest::textureCoordinates2DAsArrayPackedUnsignedNormalized, &MeshDataTest::textureCoordinates2DAsArrayPackedUnsignedNormalized, &MeshDataTest::textureCoordinates2DAsArrayPackedSignedNormalized, &MeshDataTest::textureCoordinates2DAsArrayPackedSignedNormalized, &MeshDataTest::textureCoordinates2DIntoArrayInvalidSize, &MeshDataTest::colorsAsArray, &MeshDataTest::colorsAsArray, &MeshDataTest::colorsAsArray, &MeshDataTest::colorsAsArray, &MeshDataTest::colorsAsArrayPackedUnsignedNormalized, &MeshDataTest::colorsAsArrayPackedUnsignedNormalized, &MeshDataTest::colorsAsArrayPackedUnsignedNormalized, &MeshDataTest::colorsAsArrayPackedUnsignedNormalized, &MeshDataTest::colorsIntoArrayInvalidSize, &MeshDataTest::jointIdsAsArray, &MeshDataTest::jointIdsAsArray, &MeshDataTest::jointIdsAsArray, &MeshDataTest::jointIdsIntoArrayInvalidSizeStride, &MeshDataTest::weightsAsArray, &MeshDataTest::weightsAsArray, &MeshDataTest::weightsAsArrayPackedUnsignedNormalized, &MeshDataTest::weightsAsArrayPackedUnsignedNormalized, &MeshDataTest::weightsIntoArrayInvalidSizeStride, &MeshDataTest::objectIdsAsArray, &MeshDataTest::objectIdsAsArray, &MeshDataTest::objectIdsAsArray, &MeshDataTest::objectIdsIntoArrayInvalidSize, &MeshDataTest::implementationSpecificIndexTypeWrongAccess, &MeshDataTest::implementationSpecificVertexFormatWrongAccess, &MeshDataTest::mutableAccessNotAllowed, &MeshDataTest::indicesNotIndexed, &MeshDataTest::indicesWrongType, &MeshDataTest::attributeNotFound, &MeshDataTest::attributeWrongType, &MeshDataTest::attributeWrongArrayAccess, &MeshDataTest::releaseIndexData, &MeshDataTest::releaseAttributeData, &MeshDataTest::releaseVertexData}); } void MeshDataTest::customAttributeName() { CORRADE_VERIFY(!isMeshAttributeCustom(MeshAttribute::Position)); CORRADE_VERIFY(!isMeshAttributeCustom(MeshAttribute(32767))); CORRADE_VERIFY(isMeshAttributeCustom(MeshAttribute(Implementation::MeshAttributeCustom))); CORRADE_VERIFY(isMeshAttributeCustom(MeshAttribute(65535))); CORRADE_COMPARE(UnsignedShort(meshAttributeCustom(0)), 32768); CORRADE_COMPARE(UnsignedShort(meshAttributeCustom(8290)), 41058); CORRADE_COMPARE(UnsignedShort(meshAttributeCustom(32767)), 65535); CORRADE_COMPARE(meshAttributeCustom(MeshAttribute(Implementation::MeshAttributeCustom)), 0); CORRADE_COMPARE(meshAttributeCustom(MeshAttribute(41058)), 8290); CORRADE_COMPARE(meshAttributeCustom(MeshAttribute(65535)), 32767); constexpr bool is = isMeshAttributeCustom(MeshAttribute(41058)); CORRADE_VERIFY(is); constexpr MeshAttribute a = meshAttributeCustom(8290); CORRADE_COMPARE(UnsignedShort(a), 41058); constexpr UnsignedShort b = meshAttributeCustom(a); CORRADE_COMPARE(b, 8290); } void MeshDataTest::customAttributeNameTooLarge() { CORRADE_SKIP_IF_NO_ASSERT(); std::ostringstream out; Error redirectError{&out}; meshAttributeCustom(32768); CORRADE_COMPARE(out.str(), "Trade::meshAttributeCustom(): index 32768 too large\n"); } void MeshDataTest::customAttributeNameNotCustom() { CORRADE_SKIP_IF_NO_ASSERT(); std::ostringstream out; Error redirectError{&out}; meshAttributeCustom(MeshAttribute::TextureCoordinates); CORRADE_COMPARE(out.str(), "Trade::meshAttributeCustom(): Trade::MeshAttribute::TextureCoordinates is not custom\n"); } void MeshDataTest::debugAttributeName() { std::ostringstream out; Debug{&out} << MeshAttribute::Position << meshAttributeCustom(73) << MeshAttribute(0x73); CORRADE_COMPARE(out.str(), "Trade::MeshAttribute::Position Trade::MeshAttribute::Custom(73) Trade::MeshAttribute(0x73)\n"); } void MeshDataTest::debugAttributeNamePacked() { std::ostringstream out; /* Last is not packed, ones before should not make any flags persistent */ Debug{&out} << Debug::packed << MeshAttribute::Position << Debug::packed << meshAttributeCustom(73) << Debug::packed << MeshAttribute(0x73) << MeshAttribute::Normal; CORRADE_COMPARE(out.str(), "Position Custom(73) 0x73 Trade::MeshAttribute::Normal\n"); } using namespace Math::Literals; constexpr UnsignedByte IndexBytes[]{25, 132, 3}; constexpr UnsignedShort IndexShorts[]{2575, 13224, 3}; constexpr UnsignedInt IndexInts[]{2110122, 132257, 3}; 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::StridedArrayView1D 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::StridedArrayView1D 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::StridedArrayView1D data = cindices.data(); CORRADE_COMPARE(type, MeshIndexType::UnsignedInt); CORRADE_COMPARE(data.data(), IndexInts); CORRADE_COMPARE(data.size(), 3); CORRADE_COMPARE(data.stride(), 4); } } 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 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 ctype = cindices.type(); constexpr Containers::StridedArrayView1D cdata = cindices.data(); CORRADE_COMPARE(ctype, MeshIndexType::UnsignedByte); CORRADE_COMPARE(cdata.data(), &IndexStructData[0].byteIndex); CORRADE_COMPARE(cdata.size(), 3); CORRADE_COMPARE(cdata.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 ctype = cindices.type(); constexpr Containers::StridedArrayView1D cdata = cindices.data(); CORRADE_COMPARE(ctype, MeshIndexType::UnsignedShort); CORRADE_COMPARE(cdata.data(), &IndexStructData[0].shortIndex); CORRADE_COMPARE(cdata.size(), 3); CORRADE_COMPARE(cdata.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 ctype = cindices.type(); constexpr Containers::StridedArrayView1D cdata = cindices.data(); CORRADE_COMPARE(ctype, MeshIndexType::UnsignedInt); CORRADE_COMPARE(cdata.data(), &IndexStructData[0].intIndex); CORRADE_COMPARE(cdata.size(), 3); CORRADE_COMPARE(cdata.stride(), sizeof(IndexStruct)); } } void MeshDataTest::constructIndexStridedWrongStride() { CORRADE_SKIP_IF_NO_ASSERT(); char toomuch[2*(32768 + 1)]; /* These should be fine */ MeshIndexData{Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32767}}; MeshIndexData{Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32768}.flipped<0>()}; std::ostringstream out; Error redirectError{&out}; MeshIndexData{Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32768}}; MeshIndexData{Containers::StridedArrayView1D{Containers::arrayCast(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_COMPARE(indices.data().data(), indexData); CORRADE_COMPARE(indices.data().size(), 3); CORRADE_COMPARE(indices.data().stride(), 2); } void MeshDataTest::constructIndexTypeErasedContiguousImplementationSpecificFormat() { CORRADE_SKIP_IF_NO_ASSERT(); const char indexData[3*2]{}; std::ostringstream out; Error redirectError{&out}; MeshIndexData{meshIndexTypeWrap(0xcaca), indexData}; CORRADE_COMPARE(out.str(), "Trade::MeshIndexData: can't create index data from a contiguous view and an implementation-specific type 0xcaca, pass a strided view instead\n"); } void MeshDataTest::constructIndexTypeErasedContiguousWrongSize() { CORRADE_SKIP_IF_NO_ASSERT(); const char indexData[3*2]{}; std::ostringstream out; Error redirectError{&out}; MeshIndexData{MeshIndexType::UnsignedInt, indexData}; 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 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::constructIndexTypeErasedStridedImplementationSpecificFormat() { const char indexData[3*2]{}; MeshIndexData indices{meshIndexTypeWrap(0xcaca), Containers::StridedArrayView1D{indexData, 3, 2}}; CORRADE_COMPARE(indices.type(), meshIndexTypeWrap(0xcaca)); CORRADE_COMPARE(indices.data().data(), indexData); CORRADE_COMPARE(indices.data().size(), 3); CORRADE_COMPARE(indices.data().stride(), 2); } void MeshDataTest::constructIndexTypeErasedStridedWrongStride() { CORRADE_SKIP_IF_NO_ASSERT(); char toomuch[2*(32768 + 1)]{}; /* These should be fine */ MeshIndexData{MeshIndexType::UnsignedByte, Containers::StridedArrayView1D{toomuch, 2, 32767}}; MeshIndexData{MeshIndexType::UnsignedByte, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32768}.flipped<0>()}; std::ostringstream out; Error redirectError{&out}; MeshIndexData{MeshIndexType::UnsignedByte, Containers::StridedArrayView1D{toomuch, 2, 32768}}; MeshIndexData{MeshIndexType::UnsignedByte, Containers::StridedArrayView1D{Containers::arrayCast(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 view = data; { MeshIndexData indices{Containers::arrayCast<2, const char>(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)); } { MeshIndexData indices{Containers::arrayCast<2, const char>(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)); } { MeshIndexData indices{Containers::arrayCast<2, const char>(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)); } } void MeshDataTest::constructIndex2DNotIndexed() { MeshIndexData indices{Containers::StridedArrayView2D{}}; CORRADE_COMPARE(indices.type(), MeshIndexType{}); CORRADE_COMPARE(indices.data().data(), nullptr); } void MeshDataTest::constructIndex2DWrongSize() { CORRADE_SKIP_IF_NO_ASSERT(); const char data[3*3]{}; std::ostringstream out; Error redirectError{&out}; MeshIndexData{Containers::StridedArrayView2D{data, {3, 3}}}; CORRADE_COMPARE(out.str(), "Trade::MeshIndexData: expected index type size 1, 2 or 4 but got 3\n"); } void MeshDataTest::constructIndex2DWrongStride() { CORRADE_SKIP_IF_NO_ASSERT(); char toomuch[2*(32768 + 1)]; /* These should be fine */ MeshIndexData{Containers::StridedArrayView2D{toomuch, {2, 1}, {32767, 1}}}; MeshIndexData{Containers::StridedArrayView2D{toomuch, {2, 1}, {32768, 1}}.flipped<0>()}; std::ostringstream out; Error redirectError{&out}; MeshIndexData{Containers::StridedArrayView2D{toomuch, {2, 1}, {32768, 1}}}; MeshIndexData{Containers::StridedArrayView2D{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() { CORRADE_SKIP_IF_NO_ASSERT(); const char data[3*4]{}; /* This should be fine */ MeshIndexData{Containers::StridedArrayView2D{data, {3, 2}, {4, 1}}}; std::ostringstream out; Error redirectError{&out}; MeshIndexData{Containers::StridedArrayView2D{data, {3, 2}, {4, 2}}}; CORRADE_COMPARE(out.str(), "Trade::MeshIndexData: second view dimension is not contiguous\n"); } void MeshDataTest::constructIndexNullptr() { /* Just verify it's not ambiguous */ MeshIndexData data{nullptr}; CORRADE_VERIFY(!data.data()); } constexpr Vector2 Positions[] { {1.2f, 0.2f}, {2.2f, 1.1f}, {-0.2f, 7.2f} }; void MeshDataTest::constructAttribute() { const Vector2 positionData[3]; MeshAttributeData positions{MeshAttribute::Position, Containers::arrayView(positionData)}; CORRADE_VERIFY(!positions.isOffsetOnly()); CORRADE_COMPARE(positions.arraySize(), 0); CORRADE_COMPARE(positions.name(), MeshAttribute::Position); CORRADE_COMPARE(positions.format(), VertexFormat::Vector2); CORRADE_COMPARE(positions.offset(positionData), 0); CORRADE_COMPARE(positions.stride(), sizeof(Vector2)); CORRADE_VERIFY(positions.data().data() == positionData); /* This is allowed too for simplicity, the parameter has to be large enough tho */ char someArray[3*sizeof(Vector2)]; CORRADE_VERIFY(positions.data(someArray).data() == positionData); constexpr MeshAttributeData cpositions{MeshAttribute::Position, Containers::arrayView(Positions)}; constexpr bool isOffsetOnly = cpositions.isOffsetOnly(); constexpr UnsignedShort arraySize = cpositions.arraySize(); constexpr MeshAttribute name = cpositions.name(); constexpr VertexFormat format = cpositions.format(); constexpr Short stride = cpositions.stride(); constexpr Containers::StridedArrayView1D data = cpositions.data(); CORRADE_VERIFY(!isOffsetOnly); CORRADE_COMPARE(arraySize, 0); CORRADE_COMPARE(name, MeshAttribute::Position); CORRADE_COMPARE(format, VertexFormat::Vector2); CORRADE_COMPARE(stride, sizeof(Vector2)); CORRADE_COMPARE(data.data(), Positions); } void MeshDataTest::constructAttributeDefault() { MeshAttributeData data; CORRADE_COMPARE(data.name(), MeshAttribute{}); CORRADE_COMPARE(data.format(), VertexFormat{}); constexpr MeshAttributeData cdata; CORRADE_COMPARE(cdata.name(), MeshAttribute{}); CORRADE_COMPARE(cdata.format(), VertexFormat{}); } void MeshDataTest::constructAttributeCustom() { /* Verifying it doesn't hit any assertion about disallowed type for given attribute */ const Short idData[3]{}; MeshAttributeData ids{meshAttributeCustom(13), Containers::arrayView(idData)}; CORRADE_COMPARE(ids.name(), meshAttributeCustom(13)); CORRADE_COMPARE(ids.format(), VertexFormat::Short); CORRADE_VERIFY(ids.data().data() == idData); } void MeshDataTest::constructAttribute2D() { char positionData[4*sizeof(Vector2)]{}; auto positionView = Containers::StridedArrayView2D{positionData, {4, sizeof(Vector2)}}.every(2); MeshAttributeData positions{MeshAttribute::Position, VertexFormat::Vector2, positionView}; CORRADE_VERIFY(!positions.isOffsetOnly()); CORRADE_COMPARE(positions.arraySize(), 0); CORRADE_COMPARE(positions.name(), MeshAttribute::Position); CORRADE_COMPARE(positions.format(), VertexFormat::Vector2); CORRADE_COMPARE(positions.data().data(), positionView.data()); } void MeshDataTest::constructAttribute2DWrongSize() { CORRADE_SKIP_IF_NO_ASSERT(); char positionData[4*sizeof(Vector2)]{}; std::ostringstream out; Error redirectError{&out}; MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector3, Containers::StridedArrayView2D{positionData, {4, sizeof(Vector2)}}.every(2)}; CORRADE_COMPARE(out.str(), "Trade::MeshAttributeData: second view dimension size 8 doesn't match VertexFormat::Vector3\n"); } void MeshDataTest::constructAttribute2DNonContiguous() { CORRADE_SKIP_IF_NO_ASSERT(); char positionData[4*sizeof(Vector2)]{}; std::ostringstream out; Error redirectError{&out}; MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector2, Containers::StridedArrayView2D{positionData, {2, sizeof(Vector2)*2}}.every({1, 2})}; CORRADE_COMPARE(out.str(), "Trade::MeshAttributeData: second view dimension is not contiguous\n"); } void MeshDataTest::constructAttributeTypeErased() { const Vector3 positionData[3]{}; MeshAttributeData positions{MeshAttribute::Position, VertexFormat::Vector3, Containers::arrayCast(Containers::stridedArrayView(positionData))}; CORRADE_VERIFY(!positions.isOffsetOnly()); CORRADE_COMPARE(positions.arraySize(), 0); CORRADE_COMPARE(positions.name(), MeshAttribute::Position); CORRADE_COMPARE(positions.format(), VertexFormat::Vector3); CORRADE_VERIFY(positions.data().data() == positionData); } void MeshDataTest::constructAttributeNullptr() { MeshAttributeData positions{MeshAttribute::Position, VertexFormat::Vector2, nullptr}; CORRADE_VERIFY(!positions.isOffsetOnly()); CORRADE_COMPARE(positions.arraySize(), 0); CORRADE_COMPARE(positions.name(), MeshAttribute::Position); CORRADE_COMPARE(positions.format(), VertexFormat::Vector2); CORRADE_VERIFY(!positions.data().data()); } void MeshDataTest::constructAttributePadding() { MeshAttributeData padding{-35}; CORRADE_VERIFY(!padding.isOffsetOnly()); CORRADE_COMPARE(padding.arraySize(), 0); CORRADE_COMPARE(padding.name(), MeshAttribute{}); CORRADE_COMPARE(padding.format(), VertexFormat{}); CORRADE_COMPARE(padding.data().size(), 0); CORRADE_COMPARE(padding.data().stride(), -35); CORRADE_VERIFY(!padding.data()); } void MeshDataTest::constructAttributeNonOwningArray() { const MeshAttributeData data[3]; Containers::Array array = meshAttributeDataNonOwningArray(data); CORRADE_COMPARE(array.size(), 3); CORRADE_COMPARE(static_cast(array.data()), data); } void MeshDataTest::constructAttributeOffsetOnly() { struct { Vector2 position; Vector2 textureCoordinates; } vertexData[] { {{}, {1.0f, 0.3f}}, {{}, {0.5f, 0.7f}}, }; MeshAttributeData a{MeshAttribute::TextureCoordinates, VertexFormat::Vector2, sizeof(Vector2), 2, 2*sizeof(Vector2)}; CORRADE_VERIFY(a.isOffsetOnly()); CORRADE_COMPARE(a.arraySize(), 0); CORRADE_COMPARE(a.name(), MeshAttribute::TextureCoordinates); CORRADE_COMPARE(a.format(), VertexFormat::Vector2); CORRADE_COMPARE(a.offset(vertexData), sizeof(Vector2)); CORRADE_COMPARE(a.stride(), 2*sizeof(Vector2)); CORRADE_COMPARE_AS(Containers::arrayCast(a.data(vertexData)), Containers::arrayView({{1.0f, 0.3f}, {0.5f, 0.7f}}), TestSuite::Compare::Container); constexpr MeshAttributeData ca{MeshAttribute::TextureCoordinates, VertexFormat::Vector2, sizeof(Vector2), 2, 2*sizeof(Vector2)}; CORRADE_VERIFY(ca.isOffsetOnly()); CORRADE_COMPARE(ca.arraySize(), 0); CORRADE_COMPARE(ca.name(), MeshAttribute::TextureCoordinates); CORRADE_COMPARE(ca.format(), VertexFormat::Vector2); CORRADE_COMPARE(ca.offset(vertexData), sizeof(Vector2)); CORRADE_COMPARE(ca.stride(), 2*sizeof(Vector2)); CORRADE_COMPARE_AS(Containers::arrayCast(a.data(vertexData)), Containers::arrayView({{1.0f, 0.3f}, {0.5f, 0.7f}}), TestSuite::Compare::Container); } void MeshDataTest::constructAttributeImplementationSpecificFormat() { Vector2 positions[]{{1.0f, 0.3f}, {0.5f, 0.7f}}; /* This should not fire any asserts */ MeshAttributeData a{MeshAttribute::Position, vertexFormatWrap(0x3a), positions}; CORRADE_COMPARE(a.name(), MeshAttribute::Position); CORRADE_COMPARE(a.format(), vertexFormatWrap(0x3a)); CORRADE_COMPARE_AS(Containers::arrayCast(a.data()), Containers::arrayView({{1.0f, 0.3f}, {0.5f, 0.7f}}), TestSuite::Compare::Container); } void MeshDataTest::constructAttributeWrongFormat() { CORRADE_SKIP_IF_NO_ASSERT(); Vector2 positionData[3]; std::ostringstream out; Error redirectError{&out}; MeshAttributeData{MeshAttribute::Color, Containers::arrayView(positionData)}; MeshAttributeData{MeshAttribute::Color, VertexFormat::Vector2, 0, 3, sizeof(Vector2)}; CORRADE_COMPARE(out.str(), "Trade::MeshAttributeData: VertexFormat::Vector2 is not a valid format for Trade::MeshAttribute::Color\n" "Trade::MeshAttributeData: VertexFormat::Vector2 is not a valid format for Trade::MeshAttribute::Color\n"); } void MeshDataTest::constructAttributeWrongStride() { CORRADE_SKIP_IF_NO_ASSERT(); char toomuch[2*(32768 + sizeof(Vector2))]; /* These should be fine */ MeshAttributeData{MeshAttribute::Position, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32767}}; MeshAttributeData{MeshAttribute::Position, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32768}.flipped<0>()}; MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector2, 0, 1, 32767}; MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector2, 65536, 1, -32768}; MeshAttributeData{32767}; MeshAttributeData{-32768}; std::ostringstream out; Error redirectError{&out}; MeshAttributeData{MeshAttribute::Position, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32768}}; MeshAttributeData{MeshAttribute::Position, Containers::StridedArrayView1D{Containers::arrayCast(toomuch), 2, 32769}.flipped<0>()}; MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector2, 0, 1, 32768}; MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector2, 65536, 1, -32769}; MeshAttributeData{32768}; MeshAttributeData{-32769}; CORRADE_COMPARE(out.str(), "Trade::MeshAttributeData: expected stride to fit into 16 bits but got 32768\n" "Trade::MeshAttributeData: expected stride to fit into 16 bits but got -32769\n" "Trade::MeshAttributeData: expected stride to fit into 16 bits but got 32768\n" "Trade::MeshAttributeData: expected stride to fit into 16 bits but got -32769\n" "Trade::MeshAttributeData: expected padding to fit into 16 bits but got 32768\n" "Trade::MeshAttributeData: expected padding to fit into 16 bits but got -32769\n"); } void MeshDataTest::constructAttributeOnlyArrayAllowed() { CORRADE_SKIP_IF_NO_ASSERT(); Vector2 data[3]; /* These should be fine */ MeshAttributeData{MeshAttribute::Weights, VertexFormat::Float, data, 2}; MeshAttributeData{meshAttributeCustom(25), VertexFormat::Vector2, data}; MeshAttributeData{meshAttributeCustom(25), VertexFormat::Float, data, 2}; std::ostringstream out; Error redirectError{&out}; MeshAttributeData{MeshAttribute::Weights, VertexFormat::Float, data}; CORRADE_COMPARE(out.str(), "Trade::MeshAttributeData: Trade::MeshAttribute::Weights has to be an array attribute\n"); } void MeshDataTest::constructAttributeWrongDataAccess() { CORRADE_SKIP_IF_NO_ASSERT(); Vector2 positionData[3]; MeshAttributeData a{MeshAttribute::Position, Containers::arrayView(positionData)}; MeshAttributeData b{MeshAttribute::Position, VertexFormat::Vector2, 0, 3, sizeof(Vector2)}; CORRADE_VERIFY(!a.isOffsetOnly()); CORRADE_VERIFY(b.isOffsetOnly()); a.data(positionData); /* This is fine, no asserts */ std::ostringstream out; Error redirectError{&out}; b.data(); CORRADE_COMPARE(out.str(), "Trade::MeshAttributeData::data(): the attribute is offset-only, supply a data array\n"); } constexpr Vector2 ArrayVertexData[3*4] /* MSVC 2015 needs an explicit initializer. GCC 4.8 *doesn't*. */ #ifdef CORRADE_MSVC2015_COMPATIBILITY {} #endif ; void MeshDataTest::constructArrayAttribute() { Vector2 vertexData[3*4]; Containers::StridedArrayView2D attribute{vertexData, {3, 4}}; MeshAttributeData data{meshAttributeCustom(35), attribute}; CORRADE_VERIFY(!data.isOffsetOnly()); CORRADE_COMPARE(data.name(), meshAttributeCustom(35)); CORRADE_COMPARE(data.format(), VertexFormat::Vector2); CORRADE_COMPARE(data.arraySize(), 4); CORRADE_VERIFY(data.data().data() == vertexData); CORRADE_COMPARE(data.data().size(), 3); CORRADE_COMPARE(data.data().stride(), sizeof(Vector2)*4); constexpr Containers::StridedArrayView2D cattribute{ArrayVertexData, {3, 4}}; constexpr MeshAttributeData cdata{meshAttributeCustom(35), cattribute}; CORRADE_VERIFY(!cdata.isOffsetOnly()); CORRADE_COMPARE(cdata.name(), meshAttributeCustom(35)); CORRADE_COMPARE(cdata.format(), VertexFormat::Vector2); CORRADE_COMPARE(cdata.arraySize(), 4); CORRADE_VERIFY(cdata.data().data() == ArrayVertexData); CORRADE_COMPARE(cdata.data().size(), 3); CORRADE_COMPARE(cdata.data().stride(), sizeof(Vector2)*4); } void MeshDataTest::constructArrayAttributeNonContiguous() { CORRADE_SKIP_IF_NO_ASSERT(); Vector2 vertexData[4*3]{}; std::ostringstream out; Error redirectError{&out}; MeshAttributeData{meshAttributeCustom(35), Containers::StridedArrayView2D{vertexData, {4, 3}}.every({1, 2})}; CORRADE_COMPARE(out.str(), "Trade::MeshAttributeData: second view dimension is not contiguous\n"); } void MeshDataTest::constructArrayAttribute2D() { char vertexData[3*4*sizeof(Vector2)]; MeshAttributeData data{meshAttributeCustom(35), VertexFormat::Vector2, Containers::StridedArrayView2D{vertexData, {3, 4*sizeof(Vector2)}}, 4}; CORRADE_VERIFY(!data.isOffsetOnly()); CORRADE_COMPARE(data.name(), meshAttributeCustom(35)); CORRADE_COMPARE(data.format(), VertexFormat::Vector2); CORRADE_COMPARE(data.arraySize(), 4); CORRADE_VERIFY(data.data().data() == vertexData); CORRADE_COMPARE(data.data().size(), 3); CORRADE_COMPARE(data.data().stride(), sizeof(Vector2)*4); } void MeshDataTest::constructArrayAttribute2DWrongSize() { CORRADE_SKIP_IF_NO_ASSERT(); char vertexData[3*4*sizeof(Vector2)]{}; std::ostringstream out; Error redirectError{&out}; MeshAttributeData{meshAttributeCustom(35), VertexFormat::Vector2, Containers::StridedArrayView2D{vertexData, {3, 4*sizeof(Vector2)}}, 3}; CORRADE_COMPARE(out.str(), "Trade::MeshAttributeData: second view dimension size 32 doesn't match VertexFormat::Vector2 and array size 3\n"); } void MeshDataTest::constructArrayAttribute2DNonContiguous() { CORRADE_SKIP_IF_NO_ASSERT(); char vertexData[4*3*sizeof(Vector2)]{}; std::ostringstream out; Error redirectError{&out}; MeshAttributeData{meshAttributeCustom(35), VertexFormat::Vector2, Containers::StridedArrayView2D{vertexData, {3, sizeof(Vector2)*4}}.every({1, 2}), 2}; CORRADE_COMPARE(out.str(), "Trade::MeshAttributeData: second view dimension is not contiguous\n"); } void MeshDataTest::constructArrayAttributeTypeErased() { Vector2 vertexData[3*4]; Containers::StridedArrayView1D attribute{vertexData, 3, 4*sizeof(Vector2)}; MeshAttributeData data{meshAttributeCustom(35), VertexFormat::Vector2, Containers::arrayCast(attribute), 4}; CORRADE_VERIFY(!data.isOffsetOnly()); CORRADE_COMPARE(data.name(), meshAttributeCustom(35)); CORRADE_COMPARE(data.format(), VertexFormat::Vector2); CORRADE_COMPARE(data.arraySize(), 4); CORRADE_VERIFY(data.data().data() == vertexData); CORRADE_COMPARE(data.data().size(), 3); CORRADE_COMPARE(data.data().stride(), sizeof(Vector2)*4); } void MeshDataTest::constructArrayAttributeNullptr() { MeshAttributeData positions{meshAttributeCustom(35), VertexFormat::Vector2, nullptr, 4}; CORRADE_VERIFY(!positions.isOffsetOnly()); CORRADE_COMPARE(positions.arraySize(), 4); CORRADE_COMPARE(positions.name(), meshAttributeCustom(35)); CORRADE_COMPARE(positions.format(), VertexFormat::Vector2); CORRADE_VERIFY(!positions.data().data()); } void MeshDataTest::constructArrayAttributeOffsetOnly() { MeshAttributeData data{meshAttributeCustom(35), VertexFormat::Vector2, sizeof(Vector2), 3, sizeof(Vector2), 4}; CORRADE_VERIFY(data.isOffsetOnly()); CORRADE_COMPARE(data.name(), meshAttributeCustom(35)); CORRADE_COMPARE(data.format(), VertexFormat::Vector2); CORRADE_COMPARE(data.arraySize(), 4); Vector2 vertexData[1 + 3*4]; CORRADE_VERIFY(data.data(vertexData).data() == vertexData + 1); CORRADE_COMPARE(data.data(vertexData).size(), 3); CORRADE_COMPARE(data.data(vertexData).stride(), sizeof(Vector2)); constexpr MeshAttributeData cdata{meshAttributeCustom(35), VertexFormat::Vector2, sizeof(Vector2), 3, sizeof(Vector2), 4}; CORRADE_VERIFY(cdata.isOffsetOnly()); CORRADE_COMPARE(cdata.name(), meshAttributeCustom(35)); CORRADE_COMPARE(cdata.format(), VertexFormat::Vector2); CORRADE_COMPARE(cdata.arraySize(), 4); } void MeshDataTest::constructArrayAttributeImplementationSpecificFormat() { Vector2 positions[]{{1.0f, 0.3f}, {0.5f, 0.7f}}; /* This should not fire any asserts */ MeshAttributeData a{meshAttributeCustom(35), vertexFormatWrap(0x3a), positions, 2}; CORRADE_COMPARE(a.name(), meshAttributeCustom(35)); CORRADE_COMPARE(a.format(), vertexFormatWrap(0x3a)); CORRADE_COMPARE(a.arraySize(), 2); CORRADE_COMPARE_AS(Containers::arrayCast(a.data()), Containers::arrayView({{1.0f, 0.3f}, {0.5f, 0.7f}}), TestSuite::Compare::Container); } void MeshDataTest::constructArrayAttributeNotAllowed() { CORRADE_SKIP_IF_NO_ASSERT(); Vector2 positionData[3*3]; Containers::ArrayView positions{positionData}; Containers::StridedArrayView2D positions2D{positionData, {3, 3}}; auto positions2Dchar = Containers::arrayCast<2, const char>(positions2D); /* This is all fine */ MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector2, positions, 0}; MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector2, 0, 3, 6*sizeof(Vector2), 0}; MeshAttributeData{meshAttributeCustom(35), vertexFormatWrap(0xdead), positions, 0}; MeshAttributeData{meshAttributeCustom(35), positions2D}; MeshAttributeData{meshAttributeCustom(35), VertexFormat::Vector2, positions2Dchar, 3}; MeshAttributeData{meshAttributeCustom(35), VertexFormat::Vector2, 0, 3, 6*sizeof(Vector2), 3}; MeshAttributeData{meshAttributeCustom(35), vertexFormatWrap(0xdead), positions, 3}; MeshAttributeData{meshAttributeCustom(35), vertexFormatWrap(0xdead), 0, 3, 6*sizeof(Vector2), 3}; /* This is not */ std::ostringstream out; Error redirectError{&out}; MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector2b, positions, 3}; MeshAttributeData{MeshAttribute::Position, positions2D}; MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector2, positions2Dchar, 3}; MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector2, 0, 3, 6*sizeof(Vector2), 3}; CORRADE_COMPARE(out.str(), "Trade::MeshAttributeData: Trade::MeshAttribute::Position can't be an array attribute\n" "Trade::MeshAttributeData: Trade::MeshAttribute::Position can't be an array attribute\n" "Trade::MeshAttributeData: Trade::MeshAttribute::Position can't be an array attribute\n" "Trade::MeshAttributeData: Trade::MeshAttribute::Position can't be an array attribute\n"); } void MeshDataTest::construct() { auto&& instanceData = ConstructData[testCaseInstanceId()]; setTestCaseDescription(instanceData.name); struct Vertex { Vector3 position; Vector3 normal; Vector2 textureCoordinate; Short id[2]; }; Containers::Array indexData{8*sizeof(UnsignedShort)}; auto indexView = Containers::arrayCast(indexData).slice(1, 7); indexView[0] = 0; indexView[1] = 1; indexView[2] = 2; indexView[3] = 0; indexView[4] = 2; indexView[5] = 1; /* Enough vertex data to fit also the case with large explicit vertex count (but fill just the first 3, as those are only tested) */ Containers::Array vertexData{17*sizeof(Vertex)}; auto vertexView = Containers::arrayCast(vertexData).prefix(3); vertexView[0].position = {0.1f, 0.2f, 0.3f}; vertexView[1].position = {0.4f, 0.5f, 0.6f}; vertexView[2].position = {0.7f, 0.8f, 0.9f}; vertexView[0].normal = Vector3::xAxis(); vertexView[1].normal = Vector3::yAxis(); vertexView[2].normal = Vector3::zAxis(); vertexView[0].textureCoordinate = {0.000f, 0.125f}; vertexView[1].textureCoordinate = {0.250f, 0.375f}; vertexView[2].textureCoordinate = {0.500f, 0.625f}; vertexView[0].id[0] = 15; vertexView[0].id[1] = 74; vertexView[1].id[0] = -374; vertexView[1].id[1] = 2; vertexView[2].id[0] = 22; vertexView[2].id[1] = -1; if(instanceData.vertexCount < 3) vertexView = vertexView.prefix(instanceData.vertexCount); int importerState; MeshIndexData indices{indexView}; MeshAttributeData positions{MeshAttribute::Position, Containers::StridedArrayView1D{vertexData, &vertexView[0].position, vertexView.size(), sizeof(Vertex)}}; /* Offset-only */ MeshAttributeData normals{MeshAttribute::Normal, VertexFormat::Vector3, offsetof(Vertex, normal), UnsignedInt(vertexView.size()), sizeof(Vertex)}; MeshAttributeData textureCoordinates{MeshAttribute::TextureCoordinates, Containers::StridedArrayView1D{vertexData, &vertexView[0].textureCoordinate, vertexView.size(), sizeof(Vertex)}}; /* Custom & array */ MeshAttributeData ids{meshAttributeCustom(13), Containers::StridedArrayView2D{vertexData, &vertexView[0].id[0], {vertexView.size(), 2}, {sizeof(Vertex), sizeof(Short)}}}; MeshData data{MeshPrimitive::Triangles, std::move(indexData), indices, /* Texture coordinates deliberately twice (though aliased) */ std::move(vertexData), {positions, textureCoordinates, normals, textureCoordinates, ids}, instanceData.vertexCount, &importerState}; /* Basics */ CORRADE_COMPARE(data.indexDataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(data.vertexDataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(data.primitive(), MeshPrimitive::Triangles); CORRADE_VERIFY(!data.attributeData().isEmpty()); CORRADE_COMPARE(static_cast(data.indexData() + 2), indexView.data()); CORRADE_COMPARE(static_cast(data.vertexData()), vertexView.data()); CORRADE_COMPARE(static_cast(data.mutableIndexData() + 2), indexView.data()); CORRADE_COMPARE(static_cast(data.mutableVertexData()), vertexView.data()); CORRADE_COMPARE(data.importerState(), &importerState); /* Index access */ CORRADE_VERIFY(data.isIndexed()); CORRADE_COMPARE(data.indexCount(), 6); CORRADE_COMPARE(data.indexType(), MeshIndexType::UnsignedShort); CORRADE_COMPARE(data.indexOffset(), 2); /* Typeless index access with a cast later */ CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(data.indices())[1]), 1); CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(data.indices())[3]), 0); CORRADE_COMPARE((Containers::arrayCast<1, const UnsignedShort>(data.indices())[4]), 2); /* Typed index access */ CORRADE_COMPARE(data.indices()[0], 0); CORRADE_COMPARE(data.indices()[2], 2); CORRADE_COMPARE(data.indices()[5], 1); /* Attribute access by ID */ CORRADE_COMPARE(data.vertexCount(), instanceData.expectedVertexCount); CORRADE_COMPARE(data.attributeCount(), 5); CORRADE_COMPARE(data.attributeName(0), MeshAttribute::Position); CORRADE_COMPARE(data.attributeName(1), MeshAttribute::TextureCoordinates); CORRADE_COMPARE(data.attributeName(2), MeshAttribute::Normal); CORRADE_COMPARE(data.attributeName(3), MeshAttribute::TextureCoordinates); CORRADE_COMPARE(data.attributeName(4), meshAttributeCustom(13)); CORRADE_COMPARE(data.attributeId(0), 0); CORRADE_COMPARE(data.attributeId(1), 0); CORRADE_COMPARE(data.attributeId(2), 0); CORRADE_COMPARE(data.attributeId(3), 1); CORRADE_COMPARE(data.attributeId(4), 0); CORRADE_COMPARE(data.attributeFormat(0), VertexFormat::Vector3); CORRADE_COMPARE(data.attributeFormat(1), VertexFormat::Vector2); CORRADE_COMPARE(data.attributeFormat(2), VertexFormat::Vector3); CORRADE_COMPARE(data.attributeFormat(3), VertexFormat::Vector2); CORRADE_COMPARE(data.attributeFormat(4), VertexFormat::Short); CORRADE_COMPARE(data.attributeOffset(0), 0); CORRADE_COMPARE(data.attributeOffset(1), 2*sizeof(Vector3)); CORRADE_COMPARE(data.attributeOffset(2), sizeof(Vector3)); CORRADE_COMPARE(data.attributeOffset(3), 2*sizeof(Vector3)); CORRADE_COMPARE(data.attributeOffset(4), 2*sizeof(Vector3) + sizeof(Vector2)); CORRADE_COMPARE(data.attributeStride(0), sizeof(Vertex)); CORRADE_COMPARE(data.attributeStride(1), sizeof(Vertex)); CORRADE_COMPARE(data.attributeStride(2), sizeof(Vertex)); CORRADE_COMPARE(data.attributeStride(3), sizeof(Vertex)); CORRADE_COMPARE(data.attributeArraySize(0), 0); CORRADE_COMPARE(data.attributeArraySize(1), 0); CORRADE_COMPARE(data.attributeArraySize(2), 0); CORRADE_COMPARE(data.attributeArraySize(3), 0); CORRADE_COMPARE(data.attributeArraySize(4), 2); /* Raw attribute data access by ID */ CORRADE_COMPARE(data.attributeData(1).name(), MeshAttribute::TextureCoordinates); CORRADE_COMPARE(data.attributeData(1).format(), VertexFormat::Vector2); CORRADE_COMPARE(data.attributeData(1).data().size(), instanceData.expectedVertexCount); CORRADE_COMPARE(data.attributeData(1).arraySize(), 0); if(instanceData.vertexCount) CORRADE_COMPARE(Containers::arrayCast(data.attributeData(1).data())[1], (Vector2{0.250f, 0.375f})); /* Offset-only */ CORRADE_COMPARE(data.attributeData(2).name(), MeshAttribute::Normal); CORRADE_COMPARE(data.attributeData(2).format(), VertexFormat::Vector3); CORRADE_COMPARE(data.attributeData(2).data().size(), instanceData.expectedVertexCount); CORRADE_COMPARE(data.attributeData(2).arraySize(), 0); if(instanceData.vertexCount) CORRADE_COMPARE(Containers::arrayCast(data.attributeData(2).data())[1], Vector3::yAxis()); /* Array */ CORRADE_COMPARE(data.attributeData(4).name(), meshAttributeCustom(13)); CORRADE_COMPARE(data.attributeData(4).format(), VertexFormat::Short); CORRADE_COMPARE(data.attributeData(4).data().size(), instanceData.expectedVertexCount); CORRADE_COMPARE(data.attributeData(4).arraySize(), 2); if(instanceData.vertexCount) { CORRADE_COMPARE((Containers::arrayCast<2, const Short>(data.attributeData(4).data(), 2))[1][0], -374); CORRADE_COMPARE((Containers::arrayCast<2, const Short>(data.attributeData(4).data(), 2))[1][1], 2); } /* Typeless access by ID with a cast later */ CORRADE_COMPARE(data.attribute(0).size()[0], instanceData.expectedVertexCount); CORRADE_COMPARE(data.mutableAttribute(0).size()[0], instanceData.expectedVertexCount); if(instanceData.vertexCount) { CORRADE_COMPARE((Containers::arrayCast<1, const Vector3>( data.attribute(0))[1]), (Vector3{0.4f, 0.5f, 0.6f})); CORRADE_COMPARE((Containers::arrayCast<1, const Vector2>( data.attribute(1))[0]), (Vector2{0.000f, 0.125f})); CORRADE_COMPARE((Containers::arrayCast<1, const Vector3>( data.attribute(2))[2]), Vector3::zAxis()); CORRADE_COMPARE((Containers::arrayCast<1, const Vector2>( data.attribute(3))[1]), (Vector2{0.250f, 0.375f})); /* Array */ CORRADE_COMPARE((Containers::arrayCast<2, const Short>( data.attribute(4))[0])[0], 15); CORRADE_COMPARE((Containers::arrayCast<2, const Short>( data.attribute(4))[0])[1], 74); CORRADE_COMPARE((Containers::arrayCast<1, Vector3>( data.mutableAttribute(0))[1]), (Vector3{0.4f, 0.5f, 0.6f})); CORRADE_COMPARE((Containers::arrayCast<1, Vector2>( data.mutableAttribute(1))[0]), (Vector2{0.000f, 0.125f})); CORRADE_COMPARE((Containers::arrayCast<1, Vector3>( data.mutableAttribute(2))[2]), Vector3::zAxis()); CORRADE_COMPARE((Containers::arrayCast<1, Vector2>( data.mutableAttribute(3))[1]), (Vector2{0.250f, 0.375f})); /* Array */ CORRADE_COMPARE((Containers::arrayCast<2, Short>( data.mutableAttribute(4))[0])[0], 15); CORRADE_COMPARE((Containers::arrayCast<2, Short>( data.mutableAttribute(4))[0])[1], 74); } /* Typed access by ID */ CORRADE_COMPARE(data.attribute(0).size(), instanceData.expectedVertexCount); CORRADE_COMPARE(data.mutableAttribute(0).size(), instanceData.expectedVertexCount); if(instanceData.vertexCount) { CORRADE_COMPARE(data.attribute(0)[1], (Vector3{0.4f, 0.5f, 0.6f})); CORRADE_COMPARE(data.attribute(1)[0], (Vector2{0.000f, 0.125f})); CORRADE_COMPARE(data.attribute(2)[2], Vector3::zAxis()); CORRADE_COMPARE(data.attribute(3)[1], (Vector2{0.250f, 0.375f})); /* Array */ CORRADE_COMPARE(data.attribute(4)[1][0], -374); CORRADE_COMPARE(data.attribute(4)[1][1], 2); CORRADE_COMPARE(data.mutableAttribute(0)[1], (Vector3{0.4f, 0.5f, 0.6f})); CORRADE_COMPARE(data.mutableAttribute(1)[0], (Vector2{0.000f, 0.125f})); CORRADE_COMPARE(data.mutableAttribute(2)[2], Vector3::zAxis()); CORRADE_COMPARE(data.mutableAttribute(3)[1], (Vector2{0.250f, 0.375f})); /* Array */ CORRADE_COMPARE(data.mutableAttribute(4)[1][0], -374); CORRADE_COMPARE(data.mutableAttribute(4)[1][1], 2); } /* Attribute access by name */ CORRADE_VERIFY(data.hasAttribute(MeshAttribute::Position)); CORRADE_VERIFY(data.hasAttribute(MeshAttribute::Normal)); CORRADE_VERIFY(data.hasAttribute(MeshAttribute::TextureCoordinates)); CORRADE_VERIFY(data.hasAttribute(meshAttributeCustom(13))); CORRADE_VERIFY(!data.hasAttribute(MeshAttribute::Color)); CORRADE_VERIFY(!data.hasAttribute(meshAttributeCustom(23))); CORRADE_COMPARE(data.attributeCount(MeshAttribute::Position), 1); CORRADE_COMPARE(data.attributeCount(MeshAttribute::Normal), 1); CORRADE_COMPARE(data.attributeCount(MeshAttribute::TextureCoordinates), 2); CORRADE_COMPARE(data.attributeCount(meshAttributeCustom(13)), 1); CORRADE_COMPARE(data.attributeCount(MeshAttribute::Color), 0); CORRADE_COMPARE(data.attributeCount(meshAttributeCustom(23)), 0); CORRADE_COMPARE(data.findAttributeId(MeshAttribute::Position), 0); CORRADE_COMPARE(data.attributeId(MeshAttribute::Position), 0); CORRADE_COMPARE(data.findAttributeId(MeshAttribute::Normal), 2); CORRADE_COMPARE(data.attributeId(MeshAttribute::Normal), 2); CORRADE_COMPARE(data.findAttributeId(MeshAttribute::TextureCoordinates), 1); CORRADE_COMPARE(data.attributeId(MeshAttribute::TextureCoordinates), 1); CORRADE_COMPARE(data.findAttributeId(MeshAttribute::TextureCoordinates, 1), 3); CORRADE_COMPARE(data.attributeId(MeshAttribute::TextureCoordinates, 1), 3); CORRADE_COMPARE(data.findAttributeId(meshAttributeCustom(13)), 4); CORRADE_COMPARE(data.attributeId(meshAttributeCustom(13)), 4); CORRADE_COMPARE(data.findAttributeId(MeshAttribute::Color), Containers::NullOpt); CORRADE_COMPARE(data.findAttributeId(MeshAttribute::TextureCoordinates, 2), Containers::NullOpt); CORRADE_COMPARE(data.attributeFormat(MeshAttribute::Position), VertexFormat::Vector3); CORRADE_COMPARE(data.attributeFormat(MeshAttribute::Normal), VertexFormat::Vector3); CORRADE_COMPARE(data.attributeFormat(MeshAttribute::TextureCoordinates, 0), VertexFormat::Vector2); CORRADE_COMPARE(data.attributeFormat(MeshAttribute::TextureCoordinates, 1), VertexFormat::Vector2); CORRADE_COMPARE(data.attributeFormat(meshAttributeCustom(13)), VertexFormat::Short); CORRADE_COMPARE(data.attributeOffset(MeshAttribute::Position), 0); CORRADE_COMPARE(data.attributeOffset(MeshAttribute::Normal), sizeof(Vector3)); CORRADE_COMPARE(data.attributeOffset(MeshAttribute::TextureCoordinates, 0), 2*sizeof(Vector3)); CORRADE_COMPARE(data.attributeOffset(MeshAttribute::TextureCoordinates, 1), 2*sizeof(Vector3)); CORRADE_COMPARE(data.attributeOffset(meshAttributeCustom(13)), 2*sizeof(Vector3) + sizeof(Vector2)); CORRADE_COMPARE(data.attributeStride(MeshAttribute::Position), sizeof(Vertex)); CORRADE_COMPARE(data.attributeStride(MeshAttribute::Normal), sizeof(Vertex)); CORRADE_COMPARE(data.attributeStride(MeshAttribute::TextureCoordinates, 0), sizeof(Vertex)); CORRADE_COMPARE(data.attributeStride(MeshAttribute::TextureCoordinates, 1), sizeof(Vertex)); CORRADE_COMPARE(data.attributeStride(meshAttributeCustom(13)), sizeof(Vertex)); CORRADE_COMPARE(data.attributeArraySize(MeshAttribute::Position), 0); CORRADE_COMPARE(data.attributeArraySize(MeshAttribute::Normal), 0); CORRADE_COMPARE(data.attributeArraySize(MeshAttribute::TextureCoordinates, 0), 0); CORRADE_COMPARE(data.attributeArraySize(MeshAttribute::TextureCoordinates, 1), 0); CORRADE_COMPARE(data.attributeArraySize(meshAttributeCustom(13)), 2); /* Typeless access by name with a cast later */ CORRADE_COMPARE(data.attribute(MeshAttribute::Position).size()[0], instanceData.expectedVertexCount); CORRADE_COMPARE(data.mutableAttribute(MeshAttribute::Position).size()[0], instanceData.expectedVertexCount); if(instanceData.vertexCount) { CORRADE_COMPARE((Containers::arrayCast<1, const Vector3>( data.attribute(MeshAttribute::Position))[1]), (Vector3{0.4f, 0.5f, 0.6f})); CORRADE_COMPARE((Containers::arrayCast<1, const Vector3>( data.attribute(MeshAttribute::Normal))[2]), Vector3::zAxis()); CORRADE_COMPARE((Containers::arrayCast<1, const Vector2>( data.attribute(MeshAttribute::TextureCoordinates, 0))[0]), (Vector2{0.000f, 0.125f})); CORRADE_COMPARE((Containers::arrayCast<1, const Vector2>( data.attribute(MeshAttribute::TextureCoordinates, 1))[1]), (Vector2{0.250f, 0.375f})); /* Array */ CORRADE_COMPARE((Containers::arrayCast<2, const Short>( data.attribute(meshAttributeCustom(13)))[1])[0], -374); CORRADE_COMPARE((Containers::arrayCast<2, const Short>( data.attribute(meshAttributeCustom(13)))[1])[1], 2); CORRADE_COMPARE((Containers::arrayCast<1, const Vector3>( data.mutableAttribute(MeshAttribute::Position))[1]), (Vector3{0.4f, 0.5f, 0.6f})); CORRADE_COMPARE((Containers::arrayCast<1, Vector3>( data.mutableAttribute(MeshAttribute::Normal))[2]), Vector3::zAxis()); CORRADE_COMPARE((Containers::arrayCast<1, Vector2>( data.mutableAttribute(MeshAttribute::TextureCoordinates, 0))[0]), (Vector2{0.000f, 0.125f})); CORRADE_COMPARE((Containers::arrayCast<1, Vector2>( data.mutableAttribute(MeshAttribute::TextureCoordinates, 1))[1]), (Vector2{0.250f, 0.375f})); /* Array */ CORRADE_COMPARE((Containers::arrayCast<2, Short>( data.mutableAttribute(meshAttributeCustom(13)))[1])[0], -374); CORRADE_COMPARE((Containers::arrayCast<2, Short>( data.mutableAttribute(meshAttributeCustom(13)))[1])[1], 2); } /* Typed access by name */ CORRADE_COMPARE(data.attribute(MeshAttribute::Position).size()[0], instanceData.expectedVertexCount); CORRADE_COMPARE(data.mutableAttribute(MeshAttribute::Position).size()[0], instanceData.expectedVertexCount); if(instanceData.vertexCount) { CORRADE_COMPARE(data.attribute(MeshAttribute::Position)[1], (Vector3{0.4f, 0.5f, 0.6f})); CORRADE_COMPARE(data.attribute(MeshAttribute::Normal)[2], Vector3::zAxis()); CORRADE_COMPARE(data.attribute(MeshAttribute::TextureCoordinates, 0)[0], (Vector2{0.000f, 0.125f})); CORRADE_COMPARE(data.attribute(MeshAttribute::TextureCoordinates, 1)[1], (Vector2{0.250f, 0.375f})); /* Array */ CORRADE_COMPARE(data.attribute(meshAttributeCustom(13))[2][0], 22); CORRADE_COMPARE(data.attribute(meshAttributeCustom(13))[2][1], -1); CORRADE_COMPARE(data.mutableAttribute(MeshAttribute::Position)[1], (Vector3{0.4f, 0.5f, 0.6f})); CORRADE_COMPARE(data.mutableAttribute(MeshAttribute::Normal)[2], Vector3::zAxis()); CORRADE_COMPARE(data.mutableAttribute(MeshAttribute::TextureCoordinates, 0)[0], (Vector2{0.000f, 0.125f})); CORRADE_COMPARE(data.mutableAttribute(MeshAttribute::TextureCoordinates, 1)[1], (Vector2{0.250f, 0.375f})); /* Array */ CORRADE_COMPARE(data.mutableAttribute(meshAttributeCustom(13))[2][0], 22); CORRADE_COMPARE(data.mutableAttribute(meshAttributeCustom(13))[2][1], -1); } } void MeshDataTest::constructZeroIndices() { /* This is a valid use case because this could be an empty slice of a well-defined indexed mesh. Explicitly use a non-null zero-sized array to check the importer is checking size and not pointer. */ Containers::Array vertexData{3*sizeof(Vector3)}; auto vertices = Containers::arrayCast(vertexData); char i; Containers::Array indexData{&i, 0, [](char*, std::size_t){}}; auto indices = Containers::arrayCast(indexData); MeshAttributeData positions{MeshAttribute::Position, vertices}; MeshData data{MeshPrimitive::Triangles, std::move(indexData), MeshIndexData{indices}, std::move(vertexData), {positions}}; CORRADE_COMPARE(data.indexDataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_VERIFY(data.isIndexed()); CORRADE_COMPARE(data.indexType(), MeshIndexType::UnsignedInt); CORRADE_COMPARE(data.indexCount(), 0); CORRADE_COMPARE(data.indices().size(), (Containers::Size2D{0, 4})); CORRADE_COMPARE(data.mutableIndices().size(), (Containers::Size2D{0, 4})); CORRADE_COMPARE(data.vertexCount(), 3); } void MeshDataTest::constructZeroAttributes() { /* This is a valid use case because e.g. the index/vertex data can be shared by multiple meshes and this particular one is just a plain index array */ Containers::Array indexData{3*sizeof(UnsignedInt)}; Containers::Array vertexData{3}; auto indexView = Containers::arrayCast(indexData); MeshData data{MeshPrimitive::Triangles, std::move(indexData), MeshIndexData{indexView}, std::move(vertexData), {}, 15}; CORRADE_COMPARE(data.indexCount(), 3); CORRADE_COMPARE(data.vertexDataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(data.attributeCount(), 0); CORRADE_VERIFY(!data.attributeData()); CORRADE_COMPARE(data.vertexData().size(), 3); CORRADE_COMPARE(data.vertexCount(), 15); } void MeshDataTest::constructZeroVertices() { /* This is a valid use case because this could be an empty slice of a well-defined indexed mesh */ Containers::Array indexData{3*sizeof(UnsignedInt)}; auto indexView = Containers::arrayCast(indexData); MeshAttributeData positions{MeshAttribute::Position, VertexFormat::Vector3, nullptr}; MeshData data{MeshPrimitive::Triangles, std::move(indexData), MeshIndexData{indexView}, nullptr, {positions}}; CORRADE_COMPARE(data.indexCount(), 3); CORRADE_COMPARE(data.vertexDataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(data.attributeCount(), 1); CORRADE_COMPARE(data.attributeName(0), MeshAttribute::Position); CORRADE_COMPARE(data.attributeFormat(0), VertexFormat::Vector3); CORRADE_COMPARE(data.attribute(0).size(), 0); CORRADE_VERIFY(!data.vertexData()); CORRADE_COMPARE(data.vertexCount(), 0); } void MeshDataTest::constructIndexless() { Containers::Array vertexData{3*sizeof(Vector2)}; auto vertexView = Containers::arrayCast(vertexData); vertexView[0] = {0.1f, 0.2f}; vertexView[1] = {0.4f, 0.5f}; vertexView[2] = {0.7f, 0.8f}; int importerState; MeshAttributeData positions{MeshAttribute::Position, vertexView}; MeshData data{MeshPrimitive::LineLoop, std::move(vertexData), {positions}, MeshData::ImplicitVertexCount, &importerState}; CORRADE_COMPARE(data.indexDataFlags(), DataFlag::Owned|DataFlag::Mutable); /* These are empty so it doesn't matter, but this is a nice non-restrictive default */ CORRADE_COMPARE(data.vertexDataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(data.primitive(), MeshPrimitive::LineLoop); CORRADE_COMPARE(data.importerState(), &importerState); /* Access to indexData() and typeless access to (mutable)indices() is allowed, to allow creation of MeshData instances referencing other MeshData without having to branch on isIndexed(). */ CORRADE_VERIFY(!data.isIndexed()); CORRADE_COMPARE(data.indexData(), static_cast(nullptr)); CORRADE_COMPARE(data.indices().data(), nullptr); CORRADE_COMPARE(data.indices().size(), (Containers::Size2D{0, 0})); CORRADE_COMPARE(data.mutableIndices().data(), nullptr); CORRADE_COMPARE(data.mutableIndices().size(), (Containers::Size2D{0, 0})); CORRADE_COMPARE(data.vertexCount(), 3); CORRADE_COMPARE(data.attributeCount(), 1); CORRADE_COMPARE(data.attributeFormat(MeshAttribute::Position), VertexFormat::Vector2); CORRADE_COMPARE(data.attribute(MeshAttribute::Position)[1], (Vector2{0.4f, 0.5f})); } void MeshDataTest::constructIndexlessZeroVertices() { MeshAttributeData positions{MeshAttribute::Position, VertexFormat::Vector2, nullptr}; MeshData data{MeshPrimitive::LineLoop, nullptr, {positions}}; CORRADE_COMPARE(data.primitive(), MeshPrimitive::LineLoop); CORRADE_COMPARE(data.indexData(), static_cast(nullptr)); CORRADE_COMPARE(data.vertexData(), static_cast(nullptr)); CORRADE_VERIFY(!data.isIndexed()); CORRADE_COMPARE(data.vertexCount(), 0); CORRADE_COMPARE(data.attributeCount(), 1); CORRADE_COMPARE(data.attributeFormat(MeshAttribute::Position), VertexFormat::Vector2); } void MeshDataTest::constructAttributeless() { Containers::Array indexData{6*sizeof(UnsignedInt)}; auto indexView = Containers::arrayCast(indexData); indexView[0] = 0; indexView[1] = 1; indexView[2] = 2; indexView[3] = 0; indexView[4] = 2; indexView[5] = 1; int importerState; MeshIndexData indices{indexView}; MeshData data{MeshPrimitive::TriangleStrip, std::move(indexData), indices, 3, &importerState}; /* These are empty so it doesn't matter, but this is a nice non-restrictive default */ CORRADE_COMPARE(data.indexDataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(data.vertexDataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(data.primitive(), MeshPrimitive::TriangleStrip); CORRADE_VERIFY(!data.attributeData()); CORRADE_COMPARE(data.vertexData(), static_cast(nullptr)); CORRADE_COMPARE(data.importerState(), &importerState); CORRADE_VERIFY(data.isIndexed()); CORRADE_COMPARE(data.indexCount(), 6); CORRADE_COMPARE(data.indexType(), MeshIndexType::UnsignedInt); CORRADE_COMPARE(data.indices()[0], 0); CORRADE_COMPARE(data.indices()[2], 2); CORRADE_COMPARE(data.indices()[5], 1); CORRADE_COMPARE(data.vertexCount(), 3); CORRADE_COMPARE(data.attributeCount(), 0); } void MeshDataTest::constructNotOwned() { auto&& instanceData = NotOwnedData[testCaseInstanceId()]; setTestCaseDescription(instanceData.name); UnsignedShort indexData[]{0, 1, 0}; Vector2 vertexData[]{{0.1f, 0.2f}, {0.4f, 0.5f}}; int importerState; MeshIndexData indices{indexData}; MeshAttributeData positions{MeshAttribute::Position, Containers::arrayView(vertexData)}; MeshData data{MeshPrimitive::Triangles, instanceData.indexDataFlags, Containers::arrayView(indexData), indices, instanceData.vertexDataFlags, Containers::arrayView(vertexData), {positions}, MeshData::ImplicitVertexCount, &importerState}; CORRADE_COMPARE(data.indexDataFlags(), instanceData.indexDataFlags); CORRADE_COMPARE(data.vertexDataFlags(), instanceData.vertexDataFlags); CORRADE_COMPARE(data.primitive(), MeshPrimitive::Triangles); CORRADE_COMPARE(static_cast(data.indexData()), +indexData); CORRADE_COMPARE(static_cast(data.vertexData()), +vertexData); if(instanceData.indexDataFlags & DataFlag::Mutable) CORRADE_COMPARE(static_cast(data.mutableIndexData()), +indexData); if(instanceData.vertexDataFlags & DataFlag::Mutable) CORRADE_COMPARE(static_cast(data.mutableVertexData()), +vertexData); CORRADE_COMPARE(data.importerState(), &importerState); CORRADE_VERIFY(data.isIndexed()); CORRADE_COMPARE(data.indexCount(), 3); CORRADE_COMPARE(data.indexType(), MeshIndexType::UnsignedShort); CORRADE_COMPARE(data.indices()[1], 1); CORRADE_COMPARE(data.indices()[2], 0); if(instanceData.indexDataFlags & DataFlag::Mutable) { CORRADE_COMPARE(data.mutableIndices()[1], 1); CORRADE_COMPARE(data.mutableIndices()[2], 0); } CORRADE_COMPARE(data.vertexCount(), 2); CORRADE_COMPARE(data.attributeCount(), 1); CORRADE_COMPARE(data.attributeName(0), MeshAttribute::Position); CORRADE_COMPARE(data.attributeFormat(0), VertexFormat::Vector2); CORRADE_COMPARE(data.attributeOffset(0), 0); CORRADE_COMPARE(data.attributeStride(0), sizeof(Vector2)); CORRADE_COMPARE(data.attribute(0)[0], (Vector2{0.1f, 0.2f})); CORRADE_COMPARE(data.attribute(0)[1], (Vector2{0.4f, 0.5f})); if(instanceData.vertexDataFlags & DataFlag::Mutable) { CORRADE_COMPARE(data.mutableAttribute(0)[0], (Vector2{0.1f, 0.2f})); CORRADE_COMPARE(data.mutableAttribute(0)[1], (Vector2{0.4f, 0.5f})); } } void MeshDataTest::constructIndicesNotOwned() { auto&& instanceData = SingleNotOwnedData[testCaseInstanceId()]; setTestCaseDescription(instanceData.name); UnsignedShort indexData[]{0, 1, 0}; Containers::Array vertexData{2*sizeof(Vector2)}; auto vertexView = Containers::arrayCast(vertexData); vertexView[0] = {0.1f, 0.2f}; vertexView[1] = {0.4f, 0.5f}; int importerState; MeshIndexData indices{indexData}; MeshAttributeData positions{MeshAttribute::Position, vertexView}; MeshData data{MeshPrimitive::Triangles, instanceData.dataFlags, Containers::arrayView(indexData), indices, std::move(vertexData), {positions}, MeshData::ImplicitVertexCount, &importerState}; CORRADE_COMPARE(data.indexDataFlags(), instanceData.dataFlags); CORRADE_COMPARE(data.vertexDataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(data.primitive(), MeshPrimitive::Triangles); CORRADE_COMPARE(static_cast(data.indexData()), +indexData); CORRADE_COMPARE(static_cast(data.vertexData()), vertexView.data()); if(instanceData.dataFlags & DataFlag::Mutable) CORRADE_COMPARE(static_cast(data.mutableIndexData()), +indexData); CORRADE_COMPARE(static_cast(data.mutableVertexData()), vertexView.data()); CORRADE_COMPARE(data.importerState(), &importerState); CORRADE_VERIFY(data.isIndexed()); CORRADE_COMPARE(data.indexCount(), 3); CORRADE_COMPARE(data.indexType(), MeshIndexType::UnsignedShort); CORRADE_COMPARE(data.indices()[1], 1); CORRADE_COMPARE(data.indices()[2], 0); if(instanceData.dataFlags & DataFlag::Mutable) { CORRADE_COMPARE(data.mutableIndices()[1], 1); CORRADE_COMPARE(data.mutableIndices()[2], 0); } CORRADE_COMPARE(data.vertexCount(), 2); CORRADE_COMPARE(data.attributeCount(), 1); CORRADE_COMPARE(data.attributeName(0), MeshAttribute::Position); CORRADE_COMPARE(data.attributeFormat(0), VertexFormat::Vector2); CORRADE_COMPARE(data.attributeOffset(0), 0); CORRADE_COMPARE(data.attributeStride(0), sizeof(Vector2)); CORRADE_COMPARE(data.attribute(0)[0], (Vector2{0.1f, 0.2f})); CORRADE_COMPARE(data.attribute(0)[1], (Vector2{0.4f, 0.5f})); CORRADE_COMPARE(data.mutableAttribute(0)[0], (Vector2{0.1f, 0.2f})); CORRADE_COMPARE(data.mutableAttribute(0)[1], (Vector2{0.4f, 0.5f})); } void MeshDataTest::constructVerticesNotOwned() { auto&& instanceData = SingleNotOwnedData[testCaseInstanceId()]; setTestCaseDescription(instanceData.name); Containers::Array indexData{3*sizeof(UnsignedShort)}; auto indexView = Containers::arrayCast(indexData); indexView[0] = 0; indexView[1] = 1; indexView[2] = 0; Vector2 vertexData[]{{0.1f, 0.2f}, {0.4f, 0.5f}}; int importerState; MeshIndexData indices{indexView}; MeshAttributeData positions{MeshAttribute::Position, Containers::arrayView(vertexData)}; MeshData data{MeshPrimitive::Triangles, std::move(indexData), indices, instanceData.dataFlags, Containers::arrayView(vertexData), {positions}, MeshData::ImplicitVertexCount, &importerState}; CORRADE_COMPARE(data.indexDataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(data.vertexDataFlags(), instanceData.dataFlags); CORRADE_COMPARE(data.primitive(), MeshPrimitive::Triangles); CORRADE_COMPARE(static_cast(data.indexData()), indexView.data()); CORRADE_COMPARE(static_cast(data.vertexData()), +vertexData); CORRADE_COMPARE(static_cast(data.mutableIndexData()), indexView.data()); if(instanceData.dataFlags & DataFlag::Mutable) CORRADE_COMPARE(static_cast(data.mutableVertexData()), +vertexData); CORRADE_COMPARE(data.importerState(), &importerState); CORRADE_VERIFY(data.isIndexed()); CORRADE_COMPARE(data.indexCount(), 3); CORRADE_COMPARE(data.indexType(), MeshIndexType::UnsignedShort); CORRADE_COMPARE(data.indices()[1], 1); CORRADE_COMPARE(data.indices()[2], 0); CORRADE_COMPARE(data.mutableIndices()[1], 1); CORRADE_COMPARE(data.mutableIndices()[2], 0); CORRADE_COMPARE(data.vertexCount(), 2); CORRADE_COMPARE(data.attributeCount(), 1); CORRADE_COMPARE(data.attributeName(0), MeshAttribute::Position); CORRADE_COMPARE(data.attributeFormat(0), VertexFormat::Vector2); CORRADE_COMPARE(data.attributeOffset(0), 0); CORRADE_COMPARE(data.attributeStride(0), sizeof(Vector2)); CORRADE_COMPARE(data.attribute(0)[0], (Vector2{0.1f, 0.2f})); CORRADE_COMPARE(data.attribute(0)[1], (Vector2{0.4f, 0.5f})); if(instanceData.dataFlags & DataFlag::Mutable) { CORRADE_COMPARE(data.mutableAttribute(0)[0], (Vector2{0.1f, 0.2f})); CORRADE_COMPARE(data.mutableAttribute(0)[1], (Vector2{0.4f, 0.5f})); } } void MeshDataTest::constructIndexlessNotOwned() { auto&& instanceData = SingleNotOwnedData[testCaseInstanceId()]; setTestCaseDescription(instanceData.name); Vector2 vertexData[]{{0.1f, 0.2f}, {0.4f, 0.5f}}; int importerState; MeshAttributeData positions{MeshAttribute::Position, Containers::arrayView(vertexData)}; MeshData data{MeshPrimitive::LineLoop, instanceData.dataFlags, vertexData, {positions}, MeshData::ImplicitVertexCount, &importerState}; CORRADE_COMPARE(data.indexDataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(data.vertexDataFlags(), instanceData.dataFlags); CORRADE_COMPARE(data.primitive(), MeshPrimitive::LineLoop); CORRADE_COMPARE(data.indexData(), static_cast(nullptr)); if(instanceData.dataFlags & DataFlag::Mutable) CORRADE_COMPARE(data.mutableIndexData(), static_cast(nullptr)); CORRADE_COMPARE(data.importerState(), &importerState); CORRADE_VERIFY(!data.isIndexed()); CORRADE_COMPARE(data.vertexCount(), 2); CORRADE_COMPARE(data.attributeCount(), 1); CORRADE_COMPARE(data.attributeFormat(MeshAttribute::Position), VertexFormat::Vector2); CORRADE_COMPARE(data.attribute(MeshAttribute::Position)[1], (Vector2{0.4f, 0.5f})); if(instanceData.dataFlags & DataFlag::Mutable) CORRADE_COMPARE(data.mutableAttribute(MeshAttribute::Position)[1], (Vector2{0.4f, 0.5f})); } void MeshDataTest::constructAttributelessNotOwned() { auto&& instanceData = SingleNotOwnedData[testCaseInstanceId()]; setTestCaseDescription(instanceData.name); UnsignedShort indexData[]{0, 1, 0}; int importerState; MeshIndexData indices{indexData}; MeshData data{MeshPrimitive::TriangleStrip, instanceData.dataFlags, indexData, indices, 5, &importerState}; CORRADE_COMPARE(data.indexDataFlags(), instanceData.dataFlags); CORRADE_COMPARE(data.vertexDataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(data.primitive(), MeshPrimitive::TriangleStrip); CORRADE_VERIFY(!data.attributeData()); CORRADE_COMPARE(data.vertexData(), static_cast(nullptr)); if(instanceData.dataFlags & DataFlag::Mutable) CORRADE_COMPARE(data.mutableVertexData(), nullptr); CORRADE_COMPARE(data.importerState(), &importerState); CORRADE_VERIFY(data.isIndexed()); CORRADE_COMPARE(data.indexCount(), 3); CORRADE_COMPARE(data.indexType(), MeshIndexType::UnsignedShort); CORRADE_COMPARE(data.indices()[0], 0); CORRADE_COMPARE(data.indices()[1], 1); CORRADE_COMPARE(data.indices()[2], 0); if(instanceData.dataFlags & DataFlag::Mutable) { CORRADE_COMPARE(data.mutableIndices()[0], 0); CORRADE_COMPARE(data.mutableIndices()[1], 1); CORRADE_COMPARE(data.mutableIndices()[2], 0); } CORRADE_COMPARE(data.vertexCount(), 5); CORRADE_COMPARE(data.attributeCount(), 0); } void MeshDataTest::constructIndexlessAttributeless() { int state{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */ MeshData data{MeshPrimitive::TriangleStrip, 37, &state}; /* These are both empty so it doesn't matter, but this is a nice non-restrictive default */ CORRADE_COMPARE(data.indexDataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(data.vertexDataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(data.primitive(), MeshPrimitive::TriangleStrip); CORRADE_VERIFY(!data.attributeData()); CORRADE_COMPARE(data.indexData(), static_cast(nullptr)); CORRADE_COMPARE(data.vertexData(), static_cast(nullptr)); CORRADE_COMPARE(data.importerState(), &state); CORRADE_VERIFY(!data.isIndexed()); CORRADE_COMPARE(data.vertexCount(), 37); CORRADE_COMPARE(data.attributeCount(), 0); } void MeshDataTest::constructIndexlessAttributelessZeroVertices() { int state{}; /* GCC 11 complains that "maybe uninitialized" w/o the {} */ MeshData data{MeshPrimitive::TriangleStrip, 0, &state}; CORRADE_COMPARE(data.primitive(), MeshPrimitive::TriangleStrip); CORRADE_VERIFY(!data.attributeData()); CORRADE_COMPARE(data.indexData(), static_cast(nullptr)); CORRADE_COMPARE(data.vertexData(), static_cast(nullptr)); CORRADE_COMPARE(data.importerState(), &state); CORRADE_VERIFY(!data.isIndexed()); CORRADE_COMPARE(data.vertexCount(), 0); CORRADE_COMPARE(data.attributeCount(), 0); } /* MSVC 2015 doesn't like anonymous bitfields in inline structs, so putting the declaration outside */ struct VertexWithImplementationSpecificData { Long:64; /* Using some definitely not a vertex format to test there's no weird compile-time assertion preventing this */ long double thing; }; void MeshDataTest::constructImplementationSpecificIndexType() { /* Using some definitely not an index type to test there's no weird compile-time assertion preventing this. Also using a strided view to have the same case as with implementation-specific vertex formats below -- for an implementation-specific type it's always strided, anyway. */ VertexWithImplementationSpecificData indexData[]{{12.3l}, {34.5l}, {45.6l}}; /* Constructing should work w/o asserts */ Containers::StridedArrayView1D indices{indexData, &indexData[0].thing, 3, sizeof(VertexWithImplementationSpecificData)}; MeshData data{MeshPrimitive::Triangles, DataFlag::Mutable, indexData, MeshIndexData{meshIndexTypeWrap(0xcaca), indices}, 1}; /* Getting typeless indices should work also */ CORRADE_COMPARE(data.indexType(), meshIndexTypeWrap(0xcaca)); CORRADE_COMPARE(data.indexCount(), 3); CORRADE_COMPARE(data.indexStride(), sizeof(VertexWithImplementationSpecificData)); /* The actual type size is unknown, so this will use the full stride */ CORRADE_COMPARE(data.indices().size()[1], sizeof(VertexWithImplementationSpecificData)); CORRADE_COMPARE_AS((Containers::arrayCast<1, const long double>( data.indices().prefix({3, sizeof(long double)}))), indices, TestSuite::Compare::Container); CORRADE_COMPARE_AS((Containers::arrayCast<1, const long double>( data.mutableIndices().prefix({3, sizeof(long double)}))), indices, TestSuite::Compare::Container); } void MeshDataTest::constructImplementationSpecificVertexFormat() { VertexWithImplementationSpecificData vertexData[] { {456.0l}, {456.0l} }; /* Constructing should work w/o asserts */ Containers::StridedArrayView1D attribute{vertexData, &vertexData[0].thing, 2, sizeof(VertexWithImplementationSpecificData)}; MeshData data{MeshPrimitive::TriangleFan, DataFlag::Mutable, vertexData, { MeshAttributeData{MeshAttribute::Position, vertexFormatWrap(0xdead1), attribute}, MeshAttributeData{MeshAttribute::Normal, vertexFormatWrap(0xdead2), attribute}, MeshAttributeData{MeshAttribute::TextureCoordinates, vertexFormatWrap(0xdead3), attribute}, MeshAttributeData{MeshAttribute::Color, vertexFormatWrap(0xdead4), attribute}, MeshAttributeData{meshAttributeCustom(35), vertexFormatWrap(0xdead5), attribute, 27}}}; /* Getting typeless attribute should work also */ UnsignedInt format = 0xdead1; for(MeshAttribute name: {MeshAttribute::Position, MeshAttribute::Normal, MeshAttribute::TextureCoordinates, MeshAttribute::Color, meshAttributeCustom(35)}) { CORRADE_ITERATION(name); CORRADE_COMPARE(data.attributeFormat(name), vertexFormatWrap(format++)); /* The actual type size is unknown, so this will use the full stride */ CORRADE_COMPARE(data.attribute(name).size()[1], sizeof(VertexWithImplementationSpecificData)); CORRADE_COMPARE_AS((Containers::arrayCast<1, const long double>( data.attribute(name).prefix({2, sizeof(long double)}))), attribute, TestSuite::Compare::Container); CORRADE_COMPARE_AS((Containers::arrayCast<1, const long double>( data.mutableAttribute(name).prefix({2, sizeof(long double)}))), attribute, TestSuite::Compare::Container); } } void MeshDataTest::constructSpecialIndexStrides() { /* Every second index */ { Containers::Array indexData{sizeof(UnsignedShort)*8}; Containers::StridedArrayView1D indices = Containers::arrayCast(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({1, 2, 3, 4}), TestSuite::Compare::Container); CORRADE_COMPARE_AS((Containers::arrayCast<1, UnsignedShort>(mesh.mutableIndices())), Containers::stridedArrayView({1, 2, 3, 4}), TestSuite::Compare::Container); /* Typed access */ CORRADE_COMPARE_AS(mesh.indices(), Containers::arrayView({1, 2, 3, 4}), TestSuite::Compare::Container); CORRADE_COMPARE_AS(mesh.mutableIndices(), Containers::stridedArrayView({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({1, 2, 3, 4}), TestSuite::Compare::Container); /* Zero stride. Not sure how useful like this. */ } { Containers::Array indexData{sizeof(UnsignedShort)}; Containers::StridedArrayView1D indices = Containers::arrayCast(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({15, 15, 15, 15}), TestSuite::Compare::Container); CORRADE_COMPARE_AS((Containers::arrayCast<1, UnsignedShort>(mesh.mutableIndices())), Containers::stridedArrayView({15, 15, 15, 15}), TestSuite::Compare::Container); /* Typed access */ CORRADE_COMPARE_AS(mesh.indices(), Containers::arrayView({15, 15, 15, 15}), TestSuite::Compare::Container); CORRADE_COMPARE_AS(mesh.mutableIndices(), Containers::stridedArrayView({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({15, 15, 15, 15}), TestSuite::Compare::Container); /* Negative stride */ } { Containers::Array indexData{sizeof(UnsignedShort)*4}; Containers::StridedArrayView1D indices = Containers::arrayCast(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({4, 3, 2, 1}), TestSuite::Compare::Container); CORRADE_COMPARE_AS((Containers::arrayCast<1, UnsignedShort>(mesh.mutableIndices())), Containers::stridedArrayView({4, 3, 2, 1}), TestSuite::Compare::Container); /* Typed access */ CORRADE_COMPARE_AS(mesh.indices(), Containers::arrayView({4, 3, 2, 1}), TestSuite::Compare::Container); CORRADE_COMPARE_AS(mesh.mutableIndices(), Containers::stridedArrayView({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({4, 3, 2, 1}), TestSuite::Compare::Container); } } void MeshDataTest::constructSpecialIndexStridesImplementationSpecificIndexType() { /* Same as constructSpecialIndexStrides() except for custom index types, which causes the indices() to return the full stride in second dimension */ /* Every second index */ { Containers::Array indexData{sizeof(UnsignedShort)*8}; Containers::StridedArrayView1D indices = Containers::arrayCast(indexData); Utility::copy({1, 0, 2, 0, 3, 0, 4, 0}, indices); MeshData mesh{MeshPrimitive::Points, std::move(indexData), MeshIndexData{meshIndexTypeWrap(0xcaca), indices.every(2)}, 1}; CORRADE_COMPARE(mesh.indexStride(), 4); /* Type-erased access with a cast later. The size is the whole stride, so we need to take just the prefix we want. */ CORRADE_COMPARE_AS((Containers::arrayCast<1, const UnsignedShort>(mesh.indices().prefix({mesh.indexCount(), 2}))), Containers::arrayView({1, 2, 3, 4}), TestSuite::Compare::Container); CORRADE_COMPARE_AS((Containers::arrayCast<1, UnsignedShort>(mesh.mutableIndices().prefix({mesh.indexCount(), 2}))), Containers::stridedArrayView({1, 2, 3, 4}), TestSuite::Compare::Container); /* Typed access and convenience accessors won't work here due to the implementation-specific format */ /* Zero stride. The element size is zero as well, meaning there's no way to access anything except for directly interpreting the data pointer. Which is actually as desired for implementation-specific index types. */ } { Containers::Array indexData{sizeof(UnsignedShort)}; Containers::StridedArrayView1D indices = Containers::arrayCast(indexData); indices[0] = 15; MeshData mesh{MeshPrimitive::Points, std::move(indexData), MeshIndexData{meshIndexTypeWrap(0xcaca), indices.broadcasted<0>(4)}, 1}; CORRADE_COMPARE(mesh.indexStride(), 0); CORRADE_COMPARE(mesh.indices().size(), (Containers::Size2D{4, 0})); CORRADE_COMPARE(mesh.mutableIndices().size(), (Containers::Size2D{4, 0})); CORRADE_COMPARE(mesh.indices().stride(), (Containers::Stride2D{0, 1})); CORRADE_COMPARE(mesh.mutableIndices().stride(), (Containers::Stride2D{0, 1})); CORRADE_COMPARE(*reinterpret_cast(mesh.indices().data()), 15); CORRADE_COMPARE(*reinterpret_cast(mesh.mutableIndices().data()), 15); /* Typed access and convenience accessors won't work here due to the implementation-specific format */ /* Negative stride */ } { Containers::Array indexData{sizeof(UnsignedShort)*4}; Containers::StridedArrayView1D indices = Containers::arrayCast(indexData); Utility::copy({1, 2, 3, 4}, indices); MeshData mesh{MeshPrimitive::Points, std::move(indexData), MeshIndexData{meshIndexTypeWrap(0xcaca), 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({4, 3, 2, 1}), TestSuite::Compare::Container); CORRADE_COMPARE_AS((Containers::arrayCast<1, UnsignedShort>(mesh.mutableIndices())), Containers::stridedArrayView({4, 3, 2, 1}), TestSuite::Compare::Container); /* Typed access and convenience accessors won't work here due to the implementation-specific format */ } } void MeshDataTest::constructSpecialAttributeStrides() { Containers::Array vertexData{sizeof(UnsignedShort)*5}; Containers::StridedArrayView1D vertices = Containers::arrayCast(vertexData); Utility::copy({15, 1, 2, 3, 4}, vertices); MeshData mesh{MeshPrimitive::Points, std::move(vertexData), { MeshAttributeData{MeshAttribute::ObjectId, vertices.prefix(1).broadcasted<0>(4)}, MeshAttributeData{MeshAttribute::ObjectId, vertices.exceptPrefix(1).flipped<0>()}, }}; CORRADE_COMPARE(mesh.attributeStride(0), 0); CORRADE_COMPARE(mesh.attributeStride(1), -2); /* Type-erased access with a cast later */ CORRADE_COMPARE_AS((Containers::arrayCast<1, const UnsignedShort>(mesh.attribute(0))), Containers::arrayView({15, 15, 15, 15}), TestSuite::Compare::Container); CORRADE_COMPARE_AS((Containers::arrayCast<1, UnsignedShort>(mesh.mutableAttribute(0))), Containers::stridedArrayView({15, 15, 15, 15}), TestSuite::Compare::Container); CORRADE_COMPARE_AS((Containers::arrayCast<1, const UnsignedShort>(mesh.attribute(1))), Containers::arrayView({4, 3, 2, 1}), TestSuite::Compare::Container); CORRADE_COMPARE_AS((Containers::arrayCast<1, UnsignedShort>(mesh.mutableAttribute(1))), Containers::stridedArrayView({4, 3, 2, 1}), TestSuite::Compare::Container); /* Typed access */ CORRADE_COMPARE_AS(mesh.attribute(0), Containers::arrayView({15, 15, 15, 15}), TestSuite::Compare::Container); CORRADE_COMPARE_AS(mesh.mutableAttribute(0), Containers::stridedArrayView({15, 15, 15, 15}), TestSuite::Compare::Container); CORRADE_COMPARE_AS(mesh.attribute(1), Containers::arrayView({4, 3, 2, 1}), TestSuite::Compare::Container); CORRADE_COMPARE_AS(mesh.mutableAttribute(1), Containers::stridedArrayView({4, 3, 2, 1}), TestSuite::Compare::Container); /* All convenience accessors should work well also as they consume the output of the type-erased one. But just to be sure, test at least one. */ CORRADE_COMPARE_AS(mesh.objectIdsAsArray(0), Containers::arrayView({15, 15, 15, 15}), TestSuite::Compare::Container); CORRADE_COMPARE_AS(mesh.objectIdsAsArray(1), Containers::arrayView({4, 3, 2, 1}), TestSuite::Compare::Container); } void MeshDataTest::constructSpecialAttributeStridesImplementationSpecificVertexFormat() { /* Same as constructSpecialAttributeStrides() except for custom vertex formats, which causes the attribute() to return the full stride in second dimension */ Containers::Array vertexData{sizeof(UnsignedShort)*5}; Containers::StridedArrayView1D vertices = Containers::arrayCast(vertexData); Utility::copy({15, 1, 2, 3, 4}, vertices); MeshData mesh{MeshPrimitive::Points, std::move(vertexData), { MeshAttributeData{MeshAttribute::ObjectId, vertexFormatWrap(0xdead), vertices.prefix(1).broadcasted<0>(4)}, MeshAttributeData{MeshAttribute::ObjectId, vertexFormatWrap(0xdead), vertices.exceptPrefix(1).flipped<0>()} }}; CORRADE_COMPARE(mesh.attributeStride(0), 0); CORRADE_COMPARE(mesh.attributeStride(1), -2); /* Type-erased access with a cast later. For the zero-stride attribute the element size is zero as well, meaning there's no way to access anything except for directly interpreting the data pointer. Which is actually as desired for implementation-specific vertex formats. */ CORRADE_COMPARE(mesh.attribute(0).size(), (Containers::Size2D{4, 0})); CORRADE_COMPARE(mesh.mutableAttribute(0).size(), (Containers::Size2D{4, 0})); CORRADE_COMPARE(mesh.attribute(0).stride(), (Containers::Stride2D{0, 1})); CORRADE_COMPARE(mesh.mutableAttribute(0).stride(), (Containers::Stride2D{0, 1})); CORRADE_COMPARE(*reinterpret_cast(mesh.attribute(0).data()), 15); CORRADE_COMPARE(*reinterpret_cast(mesh.mutableAttribute(0).data()), 15); CORRADE_COMPARE_AS((Containers::arrayCast<1, const UnsignedShort>(mesh.attribute(1))), Containers::arrayView({4, 3, 2, 1}), TestSuite::Compare::Container); CORRADE_COMPARE_AS((Containers::arrayCast<1, UnsignedShort>(mesh.mutableAttribute(1))), Containers::stridedArrayView({4, 3, 2, 1}), TestSuite::Compare::Container); /* Typed access and convenience accessors won't work here due to the implementation-specific format */ } void MeshDataTest::constructIndexDataButNotIndexed() { CORRADE_SKIP_IF_NO_ASSERT(); Containers::Array indexData{6}; std::ostringstream out; Error redirectError{&out}; MeshAttributeData positions{MeshAttribute::Position, VertexFormat::Vector2, nullptr}; MeshData{MeshPrimitive::Points, std::move(indexData), MeshIndexData{}, nullptr, {positions}}; CORRADE_COMPARE(out.str(), "Trade::MeshData: indexData passed for a non-indexed mesh\n"); } void MeshDataTest::constructAttributelessImplicitVertexCount() { CORRADE_SKIP_IF_NO_ASSERT(); std::ostringstream out; Error redirectError{&out}; MeshData{MeshPrimitive::Points, nullptr, {}}; CORRADE_COMPARE(out.str(), "Trade::MeshData: vertex count can't be implicit if there are no attributes\n"); } void MeshDataTest::constructIndicesNotContained() { CORRADE_SKIP_IF_NO_ASSERT(); const Containers::Array indexData{reinterpret_cast(0xbadda9), 3*sizeof(UnsignedShort), [](char*, std::size_t){}}; Containers::Array sameIndexDataButMovable{reinterpret_cast(0xbadda9), 3*sizeof(UnsignedShort), [](char*, std::size_t){}}; Containers::ArrayView indexDataSlightlyOut{reinterpret_cast(0xbaddaa), 3}; Containers::ArrayView indexDataOut{reinterpret_cast(0xdead), 3}; Containers::StridedArrayView1D indexDataStridedOut{{reinterpret_cast(0xbadda9), 6}, 3, 4}; /* "Obviously good" case */ MeshData{MeshPrimitive::Triangles, {}, indexData, MeshIndexData{Containers::arrayCast(indexData)}, 1}; /* An implementation-specific index type has a size assumed to be 0, so even though the last element starts at 0xbaddaf it's fine */ MeshData{MeshPrimitive::Triangles, {}, indexData, MeshIndexData{meshIndexTypeWrap(0xcaca), Containers::StridedArrayView1D{reinterpret_cast(0xbadda9 + sizeof(UnsignedShort)), 3}}, 1}; /* This has both stride and size zero, so it's treated as both starting and ending at 0xbaddaf */ MeshData{MeshPrimitive::Triangles, {}, indexData, MeshIndexData{meshIndexTypeWrap(0xcaca), Containers::StridedArrayView1D{{reinterpret_cast(0xbaddaf), 1}, 1}.broadcasted<0>(3)}, 1}; std::ostringstream out; Error redirectError{&out}; /* Basic "obviously wrong" case with owned index data */ MeshData{MeshPrimitive::Triangles, std::move(sameIndexDataButMovable), MeshIndexData{indexDataOut}, 1}; /* A "slightly off" view that exceeds the original by one byte */ MeshData{MeshPrimitive::Triangles, {}, indexData, MeshIndexData{indexDataSlightlyOut}, 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}; /* 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}; /* An implementation-specific index type has a size assumed to be 0, but even then this exceeds the data by one byte */ MeshData{MeshPrimitive::Triangles, {}, indexData, MeshIndexData{meshIndexTypeWrap(0xcaca), Containers::StridedArrayView1D{reinterpret_cast(0xbadda9 + sizeof(UnsignedShort) + 1), 3}}, 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}; /* In this case the implementation-specific type is treated as having a zero size, and the stride is zero as well, but since it starts one byte after, it's wrong */ MeshData{MeshPrimitive::Triangles, {}, indexData, MeshIndexData{meshIndexTypeWrap(0xcaca), Containers::StridedArrayView1D{{reinterpret_cast(0xbaddaf + 1), 1}, 1}.broadcasted<0>(3)}, 1}; CORRADE_COMPARE(out.str(), "Trade::MeshData: indices [0xdead:0xdeb3] are not contained in passed indexData array [0xbadda9:0xbaddaf]\n" "Trade::MeshData: indices [0xbaddaa:0xbaddb0] 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 [0x0:0x0]\n" "Trade::MeshData: indices [0xbaddac:0xbaddb0] are not contained in passed indexData array [0xbadda9:0xbaddaf]\n" "Trade::MeshData: indices [0xbaddaa:0xbaddb0] are not contained in passed indexData array [0xbadda9:0xbaddaf]\n" "Trade::MeshData: indices [0xbaddb0:0xbaddb0] are not contained in passed indexData array [0xbadda9:0xbaddaf]\n"); } void MeshDataTest::constructAttributeNotContained() { CORRADE_SKIP_IF_NO_ASSERT(); /* See implementationSpecificVertexFormatNotContained() below for implementation-specific formats */ const Containers::Array vertexData{reinterpret_cast(0xbadda9), 3*sizeof(Vector2), [](char*, std::size_t){}}; Containers::Array sameVertexDataButMovable{reinterpret_cast(0xbadda9), 3*sizeof(Vector2), [](char*, std::size_t){}}; Containers::ArrayView vertexDataIn{reinterpret_cast(0xbadda9), 3}; Containers::ArrayView vertexDataSlightlyOut{reinterpret_cast(0xbaddaa), 3}; Containers::ArrayView vertexDataOut{reinterpret_cast(0xdead), 3}; MeshAttributeData{MeshAttribute::Position, Containers::arrayCast(vertexData)}; /* "Obviously good" case */ MeshData{MeshPrimitive::Triangles, {}, vertexData, { MeshAttributeData{MeshAttribute::Position, vertexDataIn} }}; /* Here the original positions array is shrunk from 3 items to 2 and the vertex data too, which should work without asserting -- comparing just the original view would not pass, which is wrong */ MeshData{MeshPrimitive::Triangles, {}, vertexData.prefix(16), { MeshAttributeData{MeshAttribute::Position, vertexDataIn} }, 2}; /* An implementation-specific vertex format has a size assumed to be 0, so even though the last element starts at 0xbaddc1 it's fine */ MeshData{MeshPrimitive::Triangles, {}, vertexData, { MeshAttributeData{MeshAttribute::Position, vertexFormatWrap(0xcaca), Containers::ArrayView{reinterpret_cast(0xbadda9 + sizeof(Vector2)), 3}} }}; /* This has both stride and size zero, so it's treated as both starting and ending at 0xbaddc1 */ MeshData{MeshPrimitive::Triangles, {}, vertexData, { MeshAttributeData{MeshAttribute::Position, vertexFormatWrap(0xcaca), Containers::StridedArrayView1D{{reinterpret_cast(0xbaddc1), 1}, 1}.broadcasted<0>(3)} }}; std::ostringstream out; Error redirectError{&out}; /* Basic "obviously wrong" case with owned vertex data */ MeshData{MeshPrimitive::Triangles, std::move(sameVertexDataButMovable), { /* This is here to test that not just the first attribute gets checked and that the message shows proper ID */ MeshAttributeData{MeshAttribute::Position, vertexDataIn}, MeshAttributeData{MeshAttribute::Position, Containers::arrayView(vertexDataOut)} }}; /* A "slightly off" view that exceeds the original by one byte */ MeshData{MeshPrimitive::Triangles, {}, vertexData, { MeshAttributeData{MeshAttribute::Position, vertexDataSlightlyOut} }}; /* Here the original positions array is extended from 3 items to 4, which makes it not fit anymore, and thus an assert should hit -- comparing just the original view would pass, which is wrong */ MeshData{MeshPrimitive::Triangles, {}, vertexData, { MeshAttributeData{MeshAttribute::Position, vertexDataIn} }, 4}; /* Spanning 20 bytes originally, 25 when vertex count is changed to 5. If array size wouldn't be taken into account, it would span only 16 / 21, which fits into the vertex data size and thus wouldn't fail */ MeshData{MeshPrimitive::Triangles, {}, vertexData, { MeshAttributeData{meshAttributeCustom(37), Containers::StridedArrayView2D{Containers::arrayCast(vertexData), {4, 5}, {5, 1}}} }, 5}; /* And if we have no data at all, it doesn't try to dereference them but still checks properly */ MeshData{MeshPrimitive::Triangles, nullptr, { MeshAttributeData{MeshAttribute::Position, vertexDataIn} }}; /* Offset-only attributes with a different message */ MeshData{MeshPrimitive::Triangles, Containers::Array{24}, { MeshAttributeData{MeshAttribute::Position, VertexFormat::Vector2, 1, 3, 8} }}; /* This again spans 21 bytes if array size isn't taken into account, and 25 if it is */ MeshData{MeshPrimitive::Triangles, Containers::Array{24}, { MeshAttributeData{meshAttributeCustom(37), VertexFormat::UnsignedByte, 0, 5, 5, 5} }}; /* An implementation-specific vertex format has a size assumed to be 0, but even then this exceeds the data by one byte */ MeshData{MeshPrimitive::Triangles, {}, vertexData, { MeshAttributeData{MeshAttribute::Position, vertexFormatWrap(0xcaca), Containers::ArrayView{reinterpret_cast(0xbadda9 + sizeof(Vector2) + 1), 3}} }}; /* And the final boss, negative strides. Both only caught if the element size gets properly added to the larger offset, not just the "end". */ MeshData{MeshPrimitive::Triangles, {}, vertexData, { MeshAttributeData{MeshAttribute::Position, stridedArrayView(vertexDataSlightlyOut).flipped<0>()} }}; MeshData{MeshPrimitive::Triangles, Containers::Array{24}, { MeshAttributeData{meshAttributeCustom(37), VertexFormat::UnsignedByte, 24, 3, -8} }}; /* In this case the implementation-specific format is treated as having a zero size, and the stride is zero as well, but since it starts one byte after, it's wrong */ MeshData{MeshPrimitive::Triangles, {}, vertexData, { MeshAttributeData{MeshAttribute::Position, vertexFormatWrap(0xcaca), Containers::StridedArrayView1D{{reinterpret_cast(0xbaddc1 + 1), 1}, 1}.broadcasted<0>(3)} }}; CORRADE_COMPARE(out.str(), "Trade::MeshData: attribute 1 [0xdead:0xdec5] is not contained in passed vertexData array [0xbadda9:0xbaddc1]\n" "Trade::MeshData: attribute 0 [0xbaddaa:0xbaddc2] is not contained in passed vertexData array [0xbadda9:0xbaddc1]\n" "Trade::MeshData: attribute 0 [0xbadda9:0xbaddc9] is not contained in passed vertexData array [0xbadda9:0xbaddc1]\n" "Trade::MeshData: attribute 0 [0xbadda9:0xbaddc2] is not contained in passed vertexData array [0xbadda9:0xbaddc1]\n" "Trade::MeshData: attribute 0 [0xbadda9:0xbaddc1] is not contained in passed vertexData array [0x0:0x0]\n" "Trade::MeshData: offset-only attribute 0 spans 25 bytes but passed vertexData array has only 24\n" "Trade::MeshData: offset-only attribute 0 spans 25 bytes but passed vertexData array has only 24\n" "Trade::MeshData: attribute 0 [0xbaddb2:0xbaddc2] is not contained in passed vertexData array [0xbadda9:0xbaddc1]\n" "Trade::MeshData: attribute 0 [0xbaddaa:0xbaddc2] is not contained in passed vertexData array [0xbadda9:0xbaddc1]\n" "Trade::MeshData: offset-only attribute 0 spans 25 bytes but passed vertexData array has only 24\n" "Trade::MeshData: attribute 0 [0xbaddc2:0xbaddc2] is not contained in passed vertexData array [0xbadda9:0xbaddc1]\n" ); } void MeshDataTest::constructInconsitentVertexCount() { CORRADE_SKIP_IF_NO_ASSERT(); Containers::Array vertexData{136}; MeshAttributeData positions{MeshAttribute::Position, Containers::arrayCast(vertexData).prefix(3)}; MeshAttributeData positions2{MeshAttribute::Position, Containers::arrayCast(vertexData).prefix(2)}; std::ostringstream out; Error redirectError{&out}; /* The explicit vertex count should be ignored for the assertion message, we only check that all passed attribute arrays have the same vertex count. However, the actual "containment" of the attribute views is checked with the explicit vertex count -- see the constructAttributeNotContained() test above. */ MeshData{MeshPrimitive::Triangles, std::move(vertexData), {positions, positions2}, 17}; CORRADE_COMPARE(out.str(), "Trade::MeshData: attribute 1 has 2 vertices but 3 expected\n"); } void MeshDataTest::constructDifferentJointIdWeightCount() { CORRADE_SKIP_IF_NO_ASSERT(); struct Vertex { /* Weights required to be here by the constructor */ Float weights[2]; UnsignedByte jointIds[2]; UnsignedShort secondaryJointIds[4]; } vertices[3]{}; auto view = Containers::stridedArrayView(vertices); std::ostringstream out; Error redirectError{&out}; MeshData{MeshPrimitive::Points, {}, vertices, { /* Weights required to be here by the constructor */ MeshAttributeData{MeshAttribute::Weights, VertexFormat::Float, view.slice(&Vertex::weights), 2}, MeshAttributeData{MeshAttribute::JointIds, VertexFormat::UnsignedByte, view.slice(&Vertex::jointIds), 2}, MeshAttributeData{MeshAttribute::JointIds, VertexFormat::UnsignedShort, view.slice(&Vertex::secondaryJointIds), 4} }}; CORRADE_COMPARE(out.str(), "Trade::MeshData: expected 2 weight attributes to match joint IDs but got 1\n"); } void MeshDataTest::constructInconsistentJointIdWeightArraySizes() { CORRADE_SKIP_IF_NO_ASSERT(); struct Vertex { /* Weights required to be here by the constructor */ Float weights[2]; UnsignedByte jointIds[2]; UnsignedShort secondaryWeights[3]; /* Half together with {} makes GCC 4.8 crash */ UnsignedShort secondaryJointIds[4]; } vertices[3]{}; auto view = Containers::stridedArrayView(vertices); std::ostringstream out; Error redirectError{&out}; MeshData{MeshPrimitive::Points, {}, vertices, { /* Weights required to be here by the constructor */ MeshAttributeData{MeshAttribute::Weights, VertexFormat::Float, view.slice(&Vertex::weights), 2}, MeshAttributeData{MeshAttribute::JointIds, VertexFormat::UnsignedByte, view.slice(&Vertex::jointIds), 2}, MeshAttributeData{MeshAttribute::Weights, VertexFormat::Half, view.slice(&Vertex::secondaryWeights), 3}, MeshAttributeData{MeshAttribute::JointIds, VertexFormat::UnsignedShort, view.slice(&Vertex::secondaryJointIds), 4} }}; CORRADE_COMPARE(out.str(), "Trade::MeshData: expected 4 array items for weight attribute 1 to match joint IDs but got 3\n"); } void MeshDataTest::constructNotOwnedIndexFlagOwned() { CORRADE_SKIP_IF_NO_ASSERT(); const UnsignedShort indexData[]{0, 1, 0}; const Vector2 vertexData[]{{0.1f, 0.2f}, {0.4f, 0.5f}}; MeshIndexData indices{indexData}; MeshAttributeData positions{MeshAttribute::Position, Containers::arrayView(vertexData)}; std::ostringstream out; Error redirectError{&out}; MeshData data{MeshPrimitive::Triangles, DataFlag::Owned, indexData, indices, {}, vertexData, {positions}}; CORRADE_COMPARE(out.str(), "Trade::MeshData: can't construct with non-owned index data but Trade::DataFlag::Owned\n"); } void MeshDataTest::constructNotOwnedVertexFlagOwned() { CORRADE_SKIP_IF_NO_ASSERT(); const UnsignedShort indexData[]{0, 1, 0}; const Vector2 vertexData[]{{0.1f, 0.2f}, {0.4f, 0.5f}}; MeshIndexData indices{indexData}; MeshAttributeData positions{MeshAttribute::Position, Containers::arrayView(vertexData)}; std::ostringstream out; Error redirectError{&out}; MeshData data{MeshPrimitive::Triangles, {}, indexData, indices, DataFlag::Owned, vertexData, {positions}}; CORRADE_COMPARE(out.str(), "Trade::MeshData: can't construct with non-owned vertex data but Trade::DataFlag::Owned\n"); } void MeshDataTest::constructIndicesNotOwnedFlagOwned() { CORRADE_SKIP_IF_NO_ASSERT(); UnsignedShort indexData[]{0, 1, 0}; Containers::Array vertexData{2*sizeof(Vector2)}; auto vertexView = Containers::arrayCast(vertexData); vertexView[0] = {0.1f, 0.2f}; vertexView[1] = {0.4f, 0.5f}; MeshIndexData indices{indexData}; MeshAttributeData positions{MeshAttribute::Position, vertexView}; std::ostringstream out; Error redirectError{&out}; MeshData data{MeshPrimitive::Triangles, DataFlag::Owned, indexData, indices, std::move(vertexData), {positions}}; CORRADE_COMPARE(out.str(), "Trade::MeshData: can't construct with non-owned index data but Trade::DataFlag::Owned\n"); } void MeshDataTest::constructVerticesNotOwnedFlagOwned() { CORRADE_SKIP_IF_NO_ASSERT(); Containers::Array indexData{3*sizeof(UnsignedShort)}; auto indexView = Containers::arrayCast(indexData); indexView[0] = 0; indexView[1] = 1; indexView[2] = 0; Vector2 vertexData[]{{0.1f, 0.2f}, {0.4f, 0.5f}}; MeshIndexData indices{indexView}; MeshAttributeData positions{MeshAttribute::Position, Containers::arrayView(vertexData)}; std::ostringstream out; Error redirectError{&out}; MeshData data{MeshPrimitive::Triangles, std::move(indexData), indices, DataFlag::Owned, vertexData, {positions}}; CORRADE_COMPARE(out.str(), "Trade::MeshData: can't construct with non-owned vertex data but Trade::DataFlag::Owned\n"); } void MeshDataTest::constructIndexlessNotOwnedFlagOwned() { CORRADE_SKIP_IF_NO_ASSERT(); const Vector2 vertexData[]{{0.1f, 0.2f}, {0.4f, 0.5f}}; MeshAttributeData positions{MeshAttribute::Position, Containers::arrayView(vertexData)}; std::ostringstream out; Error redirectError{&out}; MeshData data{MeshPrimitive::Triangles, DataFlag::Owned, vertexData, {positions}}; CORRADE_COMPARE(out.str(), "Trade::MeshData: can't construct with non-owned vertex data but Trade::DataFlag::Owned\n"); } void MeshDataTest::constructAttributelessNotOwnedFlagOwned() { CORRADE_SKIP_IF_NO_ASSERT(); const UnsignedShort indexData[]{0, 1, 0}; MeshIndexData indices{indexData}; std::ostringstream out; Error redirectError{&out}; MeshData data{MeshPrimitive::Triangles, DataFlag::Owned, indexData, indices, 2}; CORRADE_COMPARE(out.str(), "Trade::MeshData: can't construct with non-owned index data but Trade::DataFlag::Owned\n"); } void MeshDataTest::constructInvalidAttributeData() { CORRADE_SKIP_IF_NO_ASSERT(); MeshAttributeData a; MeshAttributeData b{3}; std::ostringstream out; Error redirectError{&out}; MeshData{MeshPrimitive::Triangles, nullptr, {a}}; MeshData{MeshPrimitive::Triangles, nullptr, {b}}; CORRADE_COMPARE(out.str(), "Trade::MeshData: attribute 0 doesn't specify anything\n" "Trade::MeshData: attribute 0 doesn't specify anything\n"); } void MeshDataTest::constructCopy() { CORRADE_VERIFY(!std::is_copy_constructible{}); CORRADE_VERIFY(!std::is_copy_assignable{}); } void MeshDataTest::constructMove() { Containers::Array indexData{3*sizeof(UnsignedShort)}; auto indexView = Containers::arrayCast(indexData); indexView[0] = 0; indexView[1] = 1; indexView[2] = 0; Containers::Array vertexData{2*sizeof(Vector2)}; auto vertexView = Containers::arrayCast(vertexData); vertexView[0] = {0.1f, 0.2f}; vertexView[1] = {0.4f, 0.5f}; int importerState; MeshIndexData indices{indexView}; MeshAttributeData positions{MeshAttribute::Position, vertexView}; MeshData a{MeshPrimitive::Triangles, std::move(indexData), indices, std::move(vertexData), {positions}, MeshData::ImplicitVertexCount, &importerState}; MeshData b{std::move(a)}; CORRADE_COMPARE(b.indexDataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(b.vertexDataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(b.primitive(), MeshPrimitive::Triangles); CORRADE_COMPARE(static_cast(b.indexData()), indexView.data()); CORRADE_COMPARE(static_cast(b.vertexData()), vertexView.data()); CORRADE_COMPARE(b.importerState(), &importerState); CORRADE_VERIFY(b.isIndexed()); CORRADE_COMPARE(b.indexCount(), 3); CORRADE_COMPARE(b.indexType(), MeshIndexType::UnsignedShort); CORRADE_COMPARE(b.indices()[1], 1); CORRADE_COMPARE(b.indices()[2], 0); CORRADE_COMPARE(b.vertexCount(), 2); CORRADE_COMPARE(b.attributeCount(), 1); CORRADE_COMPARE(b.attributeName(0), MeshAttribute::Position); CORRADE_COMPARE(b.attributeFormat(0), VertexFormat::Vector2); CORRADE_COMPARE(b.attributeOffset(0), 0); CORRADE_COMPARE(b.attributeStride(0), sizeof(Vector2)); CORRADE_COMPARE(b.attribute(0)[0], (Vector2{0.1f, 0.2f})); CORRADE_COMPARE(b.attribute(0)[1], (Vector2{0.4f, 0.5f})); MeshData c{MeshPrimitive::LineLoop, 37}; c = std::move(b); CORRADE_COMPARE(c.indexDataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(c.vertexDataFlags(), DataFlag::Owned|DataFlag::Mutable); CORRADE_COMPARE(c.primitive(), MeshPrimitive::Triangles); CORRADE_COMPARE(static_cast(c.indexData()), indexView.data()); CORRADE_COMPARE(static_cast(c.vertexData()), vertexView.data()); CORRADE_COMPARE(c.importerState(), &importerState); CORRADE_VERIFY(c.isIndexed()); CORRADE_COMPARE(c.indexCount(), 3); CORRADE_COMPARE(c.indexType(), MeshIndexType::UnsignedShort); CORRADE_COMPARE(c.indices()[1], 1); CORRADE_COMPARE(c.indices()[2], 0); CORRADE_COMPARE(c.vertexCount(), 2); CORRADE_COMPARE(c.attributeCount(), 1); CORRADE_COMPARE(c.attributeName(0), MeshAttribute::Position); CORRADE_COMPARE(c.attributeFormat(0), VertexFormat::Vector2); CORRADE_COMPARE(c.attributeOffset(0), 0); CORRADE_COMPARE(c.attributeStride(0), sizeof(Vector2)); CORRADE_COMPARE(c.attribute(0)[0], (Vector2{0.1f, 0.2f})); CORRADE_COMPARE(c.attribute(0)[1], (Vector2{0.4f, 0.5f})); CORRADE_VERIFY(std::is_nothrow_move_constructible::value); CORRADE_VERIFY(std::is_nothrow_move_assignable::value); } template struct NameTraits; #define _c(format) template<> struct NameTraits { \ static const char* name() { return #format; } \ }; /* Scalars are in Math::TypeTraits already */ _c(Vector2) _c(Vector2h) _c(Vector2ub) _c(Vector2b) _c(Vector2us) _c(Vector2s) _c(Vector3) _c(Vector3h) _c(Vector3ub) _c(Vector3b) _c(Vector3us) _c(Vector3s) _c(Vector4) _c(Vector4h) _c(Vector4b) _c(Vector4s) _c(Color3) _c(Color3h) _c(Color3ub) _c(Color3us) _c(Color4) _c(Color4h) _c(Color4ub) _c(Color4us) #undef _c template void MeshDataTest::indicesAsArray() { setTestCaseTemplateName(Math::TypeTraits::name()); T indices[]{75, 131, 240}; MeshData data{MeshPrimitive::Points, {}, indices, MeshIndexData{indices}, 241}; CORRADE_COMPARE_AS(data.indicesAsArray(), Containers::arrayView({75, 131, 240}), TestSuite::Compare::Container); } void MeshDataTest::indicesIntoArrayInvalidSize() { CORRADE_SKIP_IF_NO_ASSERT(); UnsignedInt indices[3]{}; MeshData data{MeshPrimitive::Points, {}, indices, MeshIndexData{indices}, 1}; std::ostringstream out; Error redirectError{&out}; UnsignedInt destination[2]; data.indicesInto(destination); CORRADE_COMPARE(out.str(), "Trade::MeshData::indicesInto(): expected a view with 3 elements but got 2\n"); } template void MeshDataTest::positions2DAsArray() { setTestCaseTemplateName(NameTraits::name()); /* Testing also that it picks the correct attribute */ typedef typename T::Type TT; struct Vertex { Vector2 otherPosition; UnsignedShort objectId; T position; } vertices[]{ {{}, 0, T::pad(Math::Vector2{TT(2.0f), TT(1.0f)})}, {{}, 0, T::pad(Math::Vector2{TT(0.0f), TT(-1.0f)})}, {{}, 0, T::pad(Math::Vector2{TT(-2.0f), TT(3.0f)})} }; auto view = Containers::stridedArrayView(vertices); MeshData data{MeshPrimitive::Points, {}, vertices, { MeshAttributeData{MeshAttribute::Position, view.slice(&Vertex::otherPosition)}, MeshAttributeData{MeshAttribute::ObjectId, view.slice(&Vertex::objectId)}, MeshAttributeData{MeshAttribute::Position, view.slice(&Vertex::position)} }}; CORRADE_COMPARE_AS(data.positions2DAsArray(1), Containers::arrayView({ {2.0f, 1.0f}, {0.0f, -1.0f}, {-2.0f, 3.0f} }), TestSuite::Compare::Container); } template void MeshDataTest::positions2DAsArrayPackedUnsigned() { setTestCaseTemplateName(NameTraits::name()); typedef typename T::Type TT; T positions[]{ T::pad(Math::Vector2{2, 1}), T::pad(Math::Vector2{0, 15}), T::pad(Math::Vector2{22, 3}) }; MeshData data{MeshPrimitive::Points, {}, positions, { MeshAttributeData{MeshAttribute::Position, Containers::arrayView(positions)} }}; CORRADE_COMPARE_AS(data.positions2DAsArray(), Containers::arrayView({ {2.0f, 1.0f}, {0.0f, 15.0f}, {22.0f, 3.0f} }), TestSuite::Compare::Container); } template void MeshDataTest::positions2DAsArrayPackedSigned() { setTestCaseTemplateName(NameTraits::name()); typedef typename T::Type TT; T positions[]{ T::pad(Math::Vector2{2, 1}), T::pad(Math::Vector2{0, -15}), T::pad(Math::Vector2{-22, 3}) }; MeshData data{MeshPrimitive::Points, {}, positions, { MeshAttributeData{MeshAttribute::Position, Containers::arrayView(positions)} }}; CORRADE_COMPARE_AS(data.positions2DAsArray(), Containers::arrayView({ {2.0f, 1.0f}, {0.0f, -15.0f}, {-22.0f, 3.0f} }), TestSuite::Compare::Container); } template void MeshDataTest::positions2DAsArrayPackedUnsignedNormalized() { setTestCaseTemplateName(NameTraits::name()); typedef typename T::Type TT; T positions[]{ T::pad(Math::Vector2{Math::pack(1.0f), 0}), T::pad(Math::Vector2{0, Math::pack(1.0f)}) }; MeshData data{MeshPrimitive::Points, {}, positions, { MeshAttributeData{MeshAttribute::Position, vertexFormat(Implementation::vertexFormatFor(), T::Size, true), Containers::arrayView(positions)} }}; CORRADE_COMPARE_AS(data.positions2DAsArray(), Containers::arrayView({ {1.0f, 0.0f}, {0.0f, 1.0f} }), TestSuite::Compare::Container); } template void MeshDataTest::positions2DAsArrayPackedSignedNormalized() { setTestCaseTemplateName(NameTraits::name()); typedef typename T::Type TT; T positions[]{ T::pad(Math::Vector2{Math::pack(1.0f), 0}), T::pad(Math::Vector2{0, Math::pack(-1.0f)}) }; MeshData data{MeshPrimitive::Points, {}, positions, { MeshAttributeData{MeshAttribute::Position, vertexFormat(Implementation::vertexFormatFor(), T::Size, true), Containers::arrayView(positions)} }}; CORRADE_COMPARE_AS(data.positions2DAsArray(), Containers::arrayView({ {1.0f, 0.0f}, {0.0f, -1.0f} }), TestSuite::Compare::Container); } void MeshDataTest::positions2DIntoArrayInvalidSize() { CORRADE_SKIP_IF_NO_ASSERT(); Vector2 positions[3]{}; MeshData data{MeshPrimitive::Points, {}, positions, { MeshAttributeData{MeshAttribute::Position, Containers::arrayView(positions)} }}; std::ostringstream out; Error redirectError{&out}; Vector2 destination[2]; data.positions2DInto(destination); CORRADE_COMPARE(out.str(), "Trade::MeshData::positions2DInto(): expected a view with 3 elements but got 2\n"); } template void MeshDataTest::positions3DAsArray() { setTestCaseTemplateName(NameTraits::name()); /* Testing also that it picks the correct attribute. Needs to be sufficiently representable to have the test work also for half floats. */ typedef typename T::Type TT; struct Vertex { Vector3 otherPosition; UnsignedShort objectId; T position; } vertices[]{ {{}, 0, T::pad(Math::Vector3{TT(2.0f), TT(1.0f), TT(0.75f)})}, {{}, 0, T::pad(Math::Vector3{TT(0.0f), TT(-1.0f), TT(1.25f)})}, {{}, 0, T::pad(Math::Vector3{TT(-2.0f), TT(3.0f), TT(2.5f)})} }; auto view = Containers::stridedArrayView(vertices); MeshData data{MeshPrimitive::Points, {}, vertices, { MeshAttributeData{MeshAttribute::Position, view.slice(&Vertex::otherPosition)}, MeshAttributeData{MeshAttribute::ObjectId, view.slice(&Vertex::objectId)}, MeshAttributeData{MeshAttribute::Position, view.slice(&Vertex::position)} }}; CORRADE_COMPARE_AS(data.positions3DAsArray(1), Containers::arrayView({ Vector3::pad(Math::Vector::pad(Vector3{2.0f, 1.0f, 0.75f})), Vector3::pad(Math::Vector::pad(Vector3{0.0f, -1.0f, 1.25f})), Vector3::pad(Math::Vector::pad(Vector3{-2.0f, 3.0f, 2.5f})) }), TestSuite::Compare::Container); } template void MeshDataTest::positions3DAsArrayPackedUnsigned() { setTestCaseTemplateName(NameTraits::name()); typedef typename T::Type TT; T positions[]{ T::pad(Math::Vector3{2, 1, 135}), T::pad(Math::Vector3{0, 15, 2}), T::pad(Math::Vector3{22, 3, 192}) }; MeshData data{MeshPrimitive::Points, {}, positions, { MeshAttributeData{MeshAttribute::Position, Containers::arrayView(positions)} }}; CORRADE_COMPARE_AS(data.positions3DAsArray(), Containers::arrayView({ Vector3::pad(Math::Vector::pad(Vector3{2.0f, 1.0f, 135.0f})), Vector3::pad(Math::Vector::pad(Vector3{0.0f, 15.0f, 2.0f})), Vector3::pad(Math::Vector::pad(Vector3{22.0f, 3.0f, 192.0f})) }), TestSuite::Compare::Container); } template void MeshDataTest::positions3DAsArrayPackedSigned() { setTestCaseTemplateName(NameTraits::name()); typedef typename T::Type TT; T positions[]{ T::pad(Math::Vector3{2, 1, -117}), T::pad(Math::Vector3{0, -15, 2}), T::pad(Math::Vector3{-22, 3, 86}) }; MeshData data{MeshPrimitive::Points, {}, positions, { MeshAttributeData{MeshAttribute::Position, Containers::arrayView(positions)} }}; CORRADE_COMPARE_AS(data.positions3DAsArray(), Containers::arrayView({ Vector3::pad(Math::Vector::pad(Vector3{2.0f, 1.0f, -117.0f})), Vector3::pad(Math::Vector::pad(Vector3{0.0f, -15.0f, 2.0f})), Vector3::pad(Math::Vector::pad(Vector3{-22.0f, 3.0f, 86.0f})) }), TestSuite::Compare::Container); } template void MeshDataTest::positions3DAsArrayPackedUnsignedNormalized() { setTestCaseTemplateName(NameTraits::name()); typedef typename T::Type TT; T positions[]{ T::pad(Math::Vector3{Math::pack(1.0f), 0, Math::pack(1.0f)}), T::pad(Math::Vector3{0, Math::pack(1.0f), 0}) }; MeshData data{MeshPrimitive::Points, {}, positions, { MeshAttributeData{MeshAttribute::Position, vertexFormat(Implementation::vertexFormatFor(), T::Size, true), Containers::arrayView(positions)} }}; CORRADE_COMPARE_AS(data.positions3DAsArray(), Containers::arrayView({ Vector3::pad(Math::Vector::pad(Vector3{1.0f, 0.0f, 1.0f})), Vector3::pad(Math::Vector::pad(Vector3{0.0f, 1.0f, 0.0f})) }), TestSuite::Compare::Container); } template void MeshDataTest::positions3DAsArrayPackedSignedNormalized() { setTestCaseTemplateName(NameTraits::name()); typedef typename T::Type TT; T positions[]{ T::pad(Math::Vector3{Math::pack(1.0f), 0, Math::pack(1.0f)}), T::pad(Math::Vector3{0, Math::pack(-1.0f), 0}) }; MeshData data{MeshPrimitive::Points, {}, positions, { MeshAttributeData{MeshAttribute::Position, vertexFormat(Implementation::vertexFormatFor(), T::Size, true), Containers::arrayView(positions)} }}; CORRADE_COMPARE_AS(data.positions3DAsArray(), Containers::arrayView({ Vector3::pad(Math::Vector::pad(Vector3{1.0f, 0.0f, 1.0f})), Vector3::pad(Math::Vector::pad(Vector3{0.0f, -1.0f, 0.0f})) }), TestSuite::Compare::Container); } void MeshDataTest::positions3DIntoArrayInvalidSize() { CORRADE_SKIP_IF_NO_ASSERT(); Vector3 positions[3]{}; MeshData data{MeshPrimitive::Points, {}, positions, { MeshAttributeData{MeshAttribute::Position, Containers::arrayView(positions)} }}; std::ostringstream out; Error redirectError{&out}; Vector3 destination[2]; data.positions3DInto(destination); CORRADE_COMPARE(out.str(), "Trade::MeshData::positions3DInto(): expected a view with 3 elements but got 2\n"); } template void MeshDataTest::tangentsAsArray() { setTestCaseTemplateName(NameTraits::name()); /* Testing also that it picks the correct attribute. Needs to be sufficiently representable to have the test work also for half floats. */ typedef typename T::Type TT; struct Vertex { Vector3 otherTangent; UnsignedShort objectId; T tangent; } vertices[]{ {{}, 0, T::pad(Math::Vector3{TT(2.0f), TT(1.0f), TT(0.75f)})}, {{}, 0, T::pad(Math::Vector3{TT(0.0f), TT(-1.0f), TT(1.25f)})}, {{}, 0, T::pad(Math::Vector3{TT(-2.0f), TT(3.0f), TT(2.5f)})} }; auto view = Containers::stridedArrayView(vertices); MeshData data{MeshPrimitive::Points, {}, vertices, { MeshAttributeData{MeshAttribute::Tangent, view.slice(&Vertex::otherTangent)}, MeshAttributeData{MeshAttribute::ObjectId, view.slice(&Vertex::objectId)}, MeshAttributeData{MeshAttribute::Tangent, view.slice(&Vertex::tangent)} }}; CORRADE_COMPARE_AS(data.tangentsAsArray(1), Containers::arrayView({ {2.0f, 1.0f, 0.75f}, {0.0f, -1.0f, 1.25f}, {-2.0f, 3.0f, 2.5f}, }), TestSuite::Compare::Container); } template void MeshDataTest::tangentsAsArrayPackedSignedNormalized() { setTestCaseTemplateName(NameTraits::name()); typedef typename T::Type TT; T tangents[]{ T::pad(Math::Vector3{Math::pack(1.0f), 0, Math::pack(1.0f)}), T::pad(Math::Vector3{0, Math::pack(-1.0f), 0}) }; MeshData data{MeshPrimitive::Points, {}, tangents, { MeshAttributeData{MeshAttribute::Tangent, vertexFormat(Implementation::vertexFormatFor(), T::Size, true), Containers::arrayView(tangents)} }}; CORRADE_COMPARE_AS(data.tangentsAsArray(), Containers::arrayView({ {1.0f, 0.0f, 1.0f}, {0.0f, -1.0f, 0.0f} }), TestSuite::Compare::Container); } void MeshDataTest::tangentsIntoArrayInvalidSize() { CORRADE_SKIP_IF_NO_ASSERT(); Vector3 tangents[3]{}; MeshData data{MeshPrimitive::Points, {}, tangents, { MeshAttributeData{MeshAttribute::Tangent, Containers::arrayView(tangents)} }}; std::ostringstream out; Error redirectError{&out}; Vector3 destination[2]; data.tangentsInto(destination); CORRADE_COMPARE(out.str(), "Trade::MeshData::tangentsInto(): expected a view with 3 elements but got 2\n"); } template void MeshDataTest::bitangentSignsAsArray() { setTestCaseTemplateName(Math::TypeTraits::name()); /* Testing also that it picks the correct attribute. Needs to be sufficiently representable to have the test work also for half floats. */ struct Vertex { Vector3 otherTangent; UnsignedShort objectId; Math::Vector4 tangent; } vertices[]{ {{}, 0, {T(2.0f), T(1.0f), T(0.75f), T(-1.0f)}}, {{}, 0, {T(0.0f), T(-1.0f), T(1.25f), T(1.0f)}}, {{}, 0, {T(-2.0f), T(3.0f), T(2.5f), T(-1.0f)}} }; auto view = Containers::stridedArrayView(vertices); MeshData data{MeshPrimitive::Points, {}, vertices, { MeshAttributeData{MeshAttribute::Tangent, view.slice(&Vertex::otherTangent)}, MeshAttributeData{MeshAttribute::ObjectId, view.slice(&Vertex::objectId)}, MeshAttributeData{MeshAttribute::Tangent, view.slice(&Vertex::tangent)} }}; CORRADE_COMPARE_AS(data.bitangentSignsAsArray(1), Containers::arrayView({ -1.0f, 1.0f, -1.0f }), TestSuite::Compare::Container); } template void MeshDataTest::bitangentSignsAsArrayPackedSignedNormalized() { setTestCaseTemplateName(Math::TypeTraits::name()); Math::Vector4 tangents[]{ {Math::pack(1.0f), 0, Math::pack(1.0f), Math::pack(-1.0f)}, {0, Math::pack(-1.0f), 0, Math::pack(1.0f)} }; MeshData data{MeshPrimitive::Points, {}, tangents, { MeshAttributeData{MeshAttribute::Tangent, vertexFormat(Implementation::vertexFormatFor(), 4, true), Containers::arrayView(tangents)} }}; CORRADE_COMPARE_AS(data.bitangentSignsAsArray(), Containers::arrayView({ -1.0f, 1.0f }), TestSuite::Compare::Container); } void MeshDataTest::bitangentSignsAsArrayNotFourComponent() { CORRADE_SKIP_IF_NO_ASSERT(); Vector3s tangents[3]{}; MeshData data{MeshPrimitive::Points, {}, tangents, { MeshAttributeData{MeshAttribute::Tangent, VertexFormat::Vector3sNormalized, Containers::arrayView(tangents)} }}; std::ostringstream out; Error redirectError{&out}; Float destination[3]; data.bitangentSignsInto(destination); CORRADE_COMPARE(out.str(), "Trade::MeshData::bitangentSignsInto(): expected four-component tangents, but got VertexFormat::Vector3sNormalized\n"); } void MeshDataTest::bitangentSignsIntoArrayInvalidSize() { CORRADE_SKIP_IF_NO_ASSERT(); Vector4 tangents[3]{}; MeshData data{MeshPrimitive::Points, {}, tangents, { MeshAttributeData{MeshAttribute::Tangent, Containers::arrayView(tangents)} }}; std::ostringstream out; Error redirectError{&out}; Float destination[2]; data.bitangentSignsInto(destination); CORRADE_COMPARE(out.str(), "Trade::MeshData::bitangentSignsInto(): expected a view with 3 elements but got 2\n"); } template void MeshDataTest::bitangentsAsArray() { setTestCaseTemplateName(NameTraits::name()); /* Testing also that it picks the correct attribute. Needs to be sufficiently representable to have the test work also for half floats. */ typedef typename T::Type TT; struct Vertex { Vector3 otherBitangent; UnsignedShort objectId; T bitangent; } vertices[]{ {{}, 0, {TT(2.0f), TT(1.0f), TT(0.75f)}}, {{}, 0, {TT(0.0f), TT(-1.0f), TT(1.25f)}}, {{}, 0, {TT(-2.0f), TT(3.0f), TT(2.5f)}} }; auto view = Containers::stridedArrayView(vertices); MeshData data{MeshPrimitive::Points, {}, vertices, { MeshAttributeData{MeshAttribute::Bitangent, view.slice(&Vertex::otherBitangent)}, MeshAttributeData{MeshAttribute::ObjectId, view.slice(&Vertex::objectId)}, MeshAttributeData{MeshAttribute::Bitangent, view.slice(&Vertex::bitangent)} }}; CORRADE_COMPARE_AS(data.bitangentsAsArray(1), Containers::arrayView({ {2.0f, 1.0f, 0.75f}, {0.0f, -1.0f, 1.25f}, {-2.0f, 3.0f, 2.5f}, }), TestSuite::Compare::Container); } template void MeshDataTest::bitangentsAsArrayPackedSignedNormalized() { setTestCaseTemplateName(NameTraits::name()); typedef typename T::Type TT; T bitangents[]{ {Math::pack(1.0f), 0, Math::pack(1.0f)}, {0, Math::pack(-1.0f), 0} }; MeshData data{MeshPrimitive::Points, {}, bitangents, { MeshAttributeData{MeshAttribute::Bitangent, vertexFormat(Implementation::vertexFormatFor(), T::Size, true), Containers::arrayView(bitangents)} }}; CORRADE_COMPARE_AS(data.bitangentsAsArray(), Containers::arrayView({ {1.0f, 0.0f, 1.0f}, {0.0f, -1.0f, 0.0f} }), TestSuite::Compare::Container); } void MeshDataTest::bitangentsIntoArrayInvalidSize() { CORRADE_SKIP_IF_NO_ASSERT(); Vector3 bitangents[3]{}; MeshData data{MeshPrimitive::Points, {}, bitangents, { MeshAttributeData{MeshAttribute::Bitangent, Containers::arrayView(bitangents)} }}; std::ostringstream out; Error redirectError{&out}; Vector3 destination[2]; data.bitangentsInto(destination); CORRADE_COMPARE(out.str(), "Trade::MeshData::bitangentsInto(): expected a view with 3 elements but got 2\n"); } template void MeshDataTest::normalsAsArray() { setTestCaseTemplateName(NameTraits::name()); /* Testing also that it picks the correct attribute. Needs to be sufficiently representable to have the test work also for half floats. */ typedef typename T::Type TT; struct Vertex { Vector3 otherNormal; UnsignedShort objectId; T normal; } vertices[]{ {{}, 0, {TT(2.0f), TT(1.0f), TT(0.75f)}}, {{}, 0, {TT(0.0f), TT(-1.0f), TT(1.25f)}}, {{}, 0, {TT(-2.0f), TT(3.0f), TT(2.5f)}} }; auto view = Containers::stridedArrayView(vertices); MeshData data{MeshPrimitive::Points, {}, vertices, { MeshAttributeData{MeshAttribute::Normal, view.slice(&Vertex::otherNormal)}, MeshAttributeData{MeshAttribute::ObjectId, view.slice(&Vertex::objectId)}, MeshAttributeData{MeshAttribute::Normal, view.slice(&Vertex::normal)} }}; CORRADE_COMPARE_AS(data.normalsAsArray(1), Containers::arrayView({ {2.0f, 1.0f, 0.75f}, {0.0f, -1.0f, 1.25f}, {-2.0f, 3.0f, 2.5f}, }), TestSuite::Compare::Container); } template void MeshDataTest::normalsAsArrayPackedSignedNormalized() { setTestCaseTemplateName(NameTraits::name()); typedef typename T::Type TT; T normals[]{ {Math::pack(1.0f), 0, Math::pack(1.0f)}, {0, Math::pack(-1.0f), 0} }; MeshData data{MeshPrimitive::Points, {}, normals, { MeshAttributeData{MeshAttribute::Normal, vertexFormat(Implementation::vertexFormatFor(), T::Size, true), Containers::arrayView(normals)} }}; CORRADE_COMPARE_AS(data.normalsAsArray(), Containers::arrayView({ {1.0f, 0.0f, 1.0f}, {0.0f, -1.0f, 0.0f} }), TestSuite::Compare::Container); } void MeshDataTest::normalsIntoArrayInvalidSize() { CORRADE_SKIP_IF_NO_ASSERT(); Vector3 normals[3]{}; MeshData data{MeshPrimitive::Points, {}, normals, { MeshAttributeData{MeshAttribute::Normal, Containers::arrayView(normals)} }}; std::ostringstream out; Error redirectError{&out}; Vector3 destination[2]; data.normalsInto(destination); CORRADE_COMPARE(out.str(), "Trade::MeshData::normalsInto(): expected a view with 3 elements but got 2\n"); } template void MeshDataTest::textureCoordinates2DAsArray() { setTestCaseTemplateName(NameTraits::name()); /* Testing also that it picks the correct attribute. Needs to be sufficiently representable to have the test work also for half floats. */ typedef typename T::Type TT; struct Vertex { Vector2 otherTextureCoordinate; UnsignedShort objectId; T textureCoordinate; } vertices[]{ {{}, 0, {TT(2.0f), TT(1.0f)}}, {{}, 0, {TT(0.0f), TT(-1.0f)}}, {{}, 0, {TT(-2.0f), TT(3.0f)}} }; auto view = Containers::stridedArrayView(vertices); MeshData data{MeshPrimitive::Points, {}, vertices, { MeshAttributeData{MeshAttribute::TextureCoordinates, view.slice(&Vertex::otherTextureCoordinate)}, MeshAttributeData{MeshAttribute::ObjectId, view.slice(&Vertex::objectId)}, MeshAttributeData{MeshAttribute::TextureCoordinates, view.slice(&Vertex::textureCoordinate)} }}; CORRADE_COMPARE_AS(data.textureCoordinates2DAsArray(1), Containers::arrayView({ {2.0f, 1.0f}, {0.0f, -1.0f}, {-2.0f, 3.0f}, }), TestSuite::Compare::Container); } template void MeshDataTest::textureCoordinates2DAsArrayPackedUnsigned() { setTestCaseTemplateName(NameTraits::name()); T textureCoordinates[]{ {2, 1}, {0, 15}, {22, 3} }; MeshData data{MeshPrimitive::Points, {}, textureCoordinates, { MeshAttributeData{MeshAttribute::TextureCoordinates, Containers::arrayView(textureCoordinates)} }}; CORRADE_COMPARE_AS(data.textureCoordinates2DAsArray(), Containers::arrayView({ {2.0f, 1.0f}, {0.0f, 15.0f}, {22.0f, 3.0f} }), TestSuite::Compare::Container); } template void MeshDataTest::textureCoordinates2DAsArrayPackedSigned() { setTestCaseTemplateName(NameTraits::name()); T textureCoordinates[]{ {2, 1}, {0, -15}, {-22, 3} }; MeshData data{MeshPrimitive::Points, {}, textureCoordinates, { MeshAttributeData{MeshAttribute::TextureCoordinates, Containers::arrayView(textureCoordinates)} }}; CORRADE_COMPARE_AS(data.textureCoordinates2DAsArray(), Containers::arrayView({ {2.0f, 1.0f}, {0.0f, -15.0f}, {-22.0f, 3.0f} }), TestSuite::Compare::Container); } template void MeshDataTest::textureCoordinates2DAsArrayPackedUnsignedNormalized() { setTestCaseTemplateName(NameTraits::name()); typedef typename T::Type TT; T textureCoordinates[]{ {Math::pack(1.0f), 0}, {0, Math::pack(1.0f)} }; MeshData data{MeshPrimitive::Points, {}, textureCoordinates, { MeshAttributeData{MeshAttribute::TextureCoordinates, vertexFormat(Implementation::vertexFormatFor(), T::Size, true), Containers::arrayView(textureCoordinates)} }}; CORRADE_COMPARE_AS(data.textureCoordinates2DAsArray(), Containers::arrayView({ {1.0f, 0.0f}, {0.0f, 1.0f} }), TestSuite::Compare::Container); } template void MeshDataTest::textureCoordinates2DAsArrayPackedSignedNormalized() { setTestCaseTemplateName(NameTraits::name()); typedef typename T::Type TT; T textureCoordinates[]{ {Math::pack(1.0f), 0}, {0, Math::pack(-1.0f)} }; MeshData data{MeshPrimitive::Points, {}, textureCoordinates, { MeshAttributeData{MeshAttribute::TextureCoordinates, vertexFormat(Implementation::vertexFormatFor(), T::Size, true), Containers::arrayView(textureCoordinates)} }}; CORRADE_COMPARE_AS(data.textureCoordinates2DAsArray(), Containers::arrayView({ {1.0f, 0.0f}, {0.0f, -1.0f} }), TestSuite::Compare::Container); } void MeshDataTest::textureCoordinates2DIntoArrayInvalidSize() { CORRADE_SKIP_IF_NO_ASSERT(); Vector2 textureCoordinates[3]; MeshData data{MeshPrimitive::Points, {}, textureCoordinates, { MeshAttributeData{MeshAttribute::TextureCoordinates, Containers::arrayView(textureCoordinates)} }}; std::ostringstream out; Error redirectError{&out}; Vector2 destination[2]; data.textureCoordinates2DInto(destination); CORRADE_COMPARE(out.str(), "Trade::MeshData::textureCoordinates2DInto(): expected a view with 3 elements but got 2\n"); } template void MeshDataTest::colorsAsArray() { setTestCaseTemplateName(NameTraits::name()); /* Testing also that it picks the correct attribute. Can't use e.g. 0xff3366_rgbf because that's not representable in half-floats. */ typedef typename T::Type TT; struct Vertex { Color4 otherColor; UnsignedShort objectId; T color; } vertices[]{ {{}, 0, {TT(2.0f), TT(1.0f), TT(0.75f)}}, {{}, 0, {TT(0.0f), TT(-1.0f), TT(1.25f)}}, {{}, 0, {TT(-2.0f), TT(3.0f), TT(2.5f)}} }; auto view = Containers::stridedArrayView(vertices); MeshData data{MeshPrimitive::Points, {}, vertices, { MeshAttributeData{MeshAttribute::Color, view.slice(&Vertex::otherColor)}, MeshAttributeData{MeshAttribute::ObjectId, view.slice(&Vertex::objectId)}, MeshAttributeData{MeshAttribute::Color, view.slice(&Vertex::color)} }}; CORRADE_COMPARE_AS(data.colorsAsArray(1), Containers::arrayView({ {2.0f, 1.0f, 0.75f}, {0.0f, -1.0f, 1.25f}, {-2.0f, 3.0f, 2.5f}, }), TestSuite::Compare::Container); } template void MeshDataTest::colorsAsArrayPackedUnsignedNormalized() { setTestCaseTemplateName(NameTraits::name()); typedef typename T::Type TT; T colors[]{ T::pad(Math::Color4{Math::pack(1.0f), 0, Math::pack(1.0f), 0}), T::pad(Math::Color4{0, Math::pack(1.0f), 0, Math::pack(1.0f)}) }; MeshData data{MeshPrimitive::Points, {}, colors, { MeshAttributeData{MeshAttribute::Color, Containers::arrayView(colors)} }}; CORRADE_COMPARE_AS(data.colorsAsArray(), Containers::arrayView({ Color4::pad(Math::Vector::pad(Vector4{1.0f, 0.0f, 1.0f, 0.0f}), 1.0f), Color4::pad(Math::Vector::pad(Vector4{0.0f, 1.0f, 0.0f, 1.0f}), 1.0f) }), TestSuite::Compare::Container); } void MeshDataTest::colorsIntoArrayInvalidSize() { CORRADE_SKIP_IF_NO_ASSERT(); Color4 colors[3]{}; MeshData data{MeshPrimitive::Points, {}, colors, { MeshAttributeData{MeshAttribute::Color, Containers::arrayView(colors)} }}; std::ostringstream out; Error redirectError{&out}; Color4 destination[2]; data.colorsInto(destination); CORRADE_COMPARE(out.str(), "Trade::MeshData::colorsInto(): expected a view with 3 elements but got 2\n"); } template void MeshDataTest::jointIdsAsArray() { setTestCaseTemplateName(Math::TypeTraits::name()); /* Testing also that it picks the correct attribute. Needs to be sufficiently representable to have the test work also for half floats. */ struct Vertex { /* Weights required to be here by the constructor */ Float otherWeights[3]; Float weights[5]; UnsignedInt otherJointIds[3]; UnsignedShort objectId; T jointIds[5]; } vertices[]{ {{}, {}, {}, 0, {T(0), T(3), T(20), T(1), T(7)}}, {{}, {}, {}, 0, {T(9), T(1), T(15), T(2), T(3)}}, {{}, {}, {}, 0, {T(25), T(7), T(0), T(2), T(1)}}, }; auto view = Containers::stridedArrayView(vertices); MeshData data{MeshPrimitive::Points, {}, vertices, { /* Weights required to be here by the constructor */ MeshAttributeData{MeshAttribute::Weights, VertexFormat::Float, view.slice(&Vertex::otherWeights), 3}, MeshAttributeData{MeshAttribute::Weights, VertexFormat::Float, view.slice(&Vertex::weights), 5}, MeshAttributeData{MeshAttribute::JointIds, Implementation::vertexFormatFor(), view.slice(&Vertex::otherJointIds), 3}, MeshAttributeData{MeshAttribute::ObjectId, view.slice(&Vertex::objectId)}, MeshAttributeData{MeshAttribute::JointIds, Implementation::vertexFormatFor(), view.slice(&Vertex::jointIds), 5}, }}; CORRADE_COMPARE_AS(data.jointIdsAsArray(1), (Containers::arrayView({ 0, 3, 20, 1, 7, 9, 1, 15, 2, 3, 25, 7, 0, 2, 1 })), TestSuite::Compare::Container); } void MeshDataTest::jointIdsIntoArrayInvalidSizeStride() { CORRADE_SKIP_IF_NO_ASSERT(); struct Vertex { /* Weights required to be here by the constructor */ Float weights[2]; UnsignedByte jointIds[2]; } vertices[3]{}; auto view = Containers::stridedArrayView(vertices); MeshData data{MeshPrimitive::Points, {}, vertices, { /* Weights required to be here by the constructor */ MeshAttributeData{MeshAttribute::Weights, VertexFormat::Float, view.slice(&Vertex::weights), 2}, MeshAttributeData{MeshAttribute::JointIds, VertexFormat::UnsignedByte, view.slice(&Vertex::jointIds), 2} }}; std::ostringstream out; Error redirectError{&out}; UnsignedInt jointIds1[3*3]; UnsignedInt jointIds2[2*2]; UnsignedInt jointIds3[3*4]; data.jointIdsInto(Containers::StridedArrayView2D{jointIds1, {3, 3}}); data.jointIdsInto(Containers::StridedArrayView2D{jointIds2, {2, 2}}); data.jointIdsInto(Containers::StridedArrayView2D{jointIds3, {3, 4}}.every({1, 2})); CORRADE_COMPARE(out.str(), "Trade::MeshData::jointIdsInto(): expected a view with {3, 2} elements but got {3, 3}\n" "Trade::MeshData::jointIdsInto(): expected a view with {3, 2} elements but got {2, 2}\n" "Trade::MeshData::jointIdsInto(): second view dimension is not contiguous\n"); } template void MeshDataTest::weightsAsArray() { setTestCaseTemplateName(Math::TypeTraits::name()); /* Testing also that it picks the correct attribute. Needs to be sufficiently representable to have the test work also for half floats. */ struct Vertex { /* Joint IDs required to be here by the constructor */ UnsignedInt otherJointIds[3]; UnsignedInt jointIds[5]; Float otherWeights[3]; UnsignedShort objectId; T weights[5]; } vertices[]{ {{}, {}, {}, 0, {T(2.0f), T(1.0f), T(0.75f), T(3.0f), T(1.75f)}}, {{}, {}, {}, 0, {T(0.0f), T(-1.0f), T(1.25f), T(1.0f), T(2.25f)}}, {{}, {}, {}, 0, {T(-2.0f), T(3.0f), T(2.5f), T(2.5f), T(0.25f)}}, }; auto view = Containers::stridedArrayView(vertices); MeshData data{MeshPrimitive::Points, {}, vertices, { /* Joint IDs required to be here by the constructor */ MeshAttributeData{MeshAttribute::JointIds, VertexFormat::UnsignedInt, view.slice(&Vertex::otherJointIds), 3}, MeshAttributeData{MeshAttribute::JointIds, VertexFormat::UnsignedInt, view.slice(&Vertex::jointIds), 5}, MeshAttributeData{MeshAttribute::Weights, Implementation::vertexFormatFor(), view.slice(&Vertex::otherWeights), 3}, MeshAttributeData{MeshAttribute::ObjectId, view.slice(&Vertex::objectId)}, MeshAttributeData{MeshAttribute::Weights, Implementation::vertexFormatFor(), view.slice(&Vertex::weights), 5} }}; CORRADE_COMPARE_AS(data.weightsAsArray(1), (Containers::arrayView({ 2.0f, 1.0f, 0.75f, 3.0f, 1.75f, 0.0f, -1.0f, 1.25f, 1.0f, 2.25f, -2.0f, 3.0f, 2.5f, 2.5f, 0.25f, })), TestSuite::Compare::Container); } template void MeshDataTest::weightsAsArrayPackedUnsignedNormalized() { setTestCaseTemplateName(Math::TypeTraits::name()); struct Vertex { /* Joint IDs required to be here by the constructor */ UnsignedByte jointIds[2]; T weights[2]; } vertices[2]{ {{}, {Math::pack(1.0f), Math::pack(0.8f)}}, {{}, {0, Math::pack(0.4f)}} }; auto view = Containers::stridedArrayView(vertices); MeshData data{MeshPrimitive::Points, {}, vertices, { /* Joint IDs required to be here by the constructor */ MeshAttributeData{MeshAttribute::JointIds, VertexFormat::UnsignedByte, view.slice(&Vertex::jointIds), 2}, MeshAttributeData{MeshAttribute::Weights, vertexFormat(Implementation::vertexFormatFor(), 1, true), view.slice(&Vertex::weights), 2} }}; CORRADE_COMPARE_AS(data.weightsAsArray(), (Containers::arrayView({ 1.0f, 0.8f, 0.0f, 0.4f })), TestSuite::Compare::Container); } void MeshDataTest::weightsIntoArrayInvalidSizeStride() { CORRADE_SKIP_IF_NO_ASSERT(); struct Vertex { /* Joint IDs required to be here by the constructor */ UnsignedInt jointIds[2]; UnsignedShort weights[2]; /* Half together with {} makes GCC 4.8 crash */ } vertices[3]{}; auto view = Containers::stridedArrayView(vertices); MeshData data{MeshPrimitive::Points, {}, vertices, { /* Joint IDs required to be here by the constructor */ MeshAttributeData{MeshAttribute::JointIds, VertexFormat::UnsignedInt, view.slice(&Vertex::jointIds), 2}, MeshAttributeData{MeshAttribute::Weights, VertexFormat::Half, view.slice(&Vertex::weights), 2} }}; std::ostringstream out; Error redirectError{&out}; Float weights1[3*3]; Float weights2[2*2]; Float weights3[3*4]; data.weightsInto(Containers::StridedArrayView2D{weights1, {3, 3}}); data.weightsInto(Containers::StridedArrayView2D{weights2, {2, 2}}); data.weightsInto(Containers::StridedArrayView2D{weights3, {3, 4}}.every({1, 2})); CORRADE_COMPARE(out.str(), "Trade::MeshData::weightsInto(): expected a view with {3, 2} elements but got {3, 3}\n" "Trade::MeshData::weightsInto(): expected a view with {3, 2} elements but got {2, 2}\n" "Trade::MeshData::weightsInto(): second view dimension is not contiguous\n"); } template void MeshDataTest::objectIdsAsArray() { setTestCaseTemplateName(Math::TypeTraits::name()); /* Testing also that it picks the correct attribute */ struct Vertex { UnsignedByte otherObjectId; Vector2 position; T objectId; } vertices[]{ {0, {}, 157}, {0, {}, 24}, {0, {}, 1} }; auto view = Containers::stridedArrayView(vertices); MeshData data{MeshPrimitive::Points, {}, vertices, { MeshAttributeData{MeshAttribute::ObjectId, view.slice(&Vertex::otherObjectId)}, MeshAttributeData{MeshAttribute::Position, view.slice(&Vertex::position)}, MeshAttributeData{MeshAttribute::ObjectId, view.slice(&Vertex::objectId)} }}; CORRADE_COMPARE_AS(data.objectIdsAsArray(1), Containers::arrayView({ 157, 24, 1 }), TestSuite::Compare::Container); } void MeshDataTest::objectIdsIntoArrayInvalidSize() { CORRADE_SKIP_IF_NO_ASSERT(); UnsignedInt objectIds[3]{}; MeshData data{MeshPrimitive::Points, {}, objectIds, { MeshAttributeData{MeshAttribute::ObjectId, Containers::arrayView(objectIds)} }}; std::ostringstream out; Error redirectError{&out}; UnsignedInt destination[2]; data.objectIdsInto(destination); CORRADE_COMPARE(out.str(), "Trade::MeshData::objectIdsInto(): expected a view with 3 elements but got 2\n"); } void MeshDataTest::implementationSpecificIndexTypeWrongAccess() { CORRADE_SKIP_IF_NO_ASSERT(); VertexWithImplementationSpecificData indexData[3]; Containers::StridedArrayView1D indices{indexData, &indexData[0].thing, 3, sizeof(VertexWithImplementationSpecificData)}; MeshData data{MeshPrimitive::Triangles, DataFlag::Mutable, indexData, MeshIndexData{meshIndexTypeWrap(0xcaca), indices}, 1}; std::ostringstream out; Error redirectError{&out}; data.indices(); data.mutableIndices(); data.indicesAsArray(); CORRADE_COMPARE(out.str(), "Trade::MeshData::indices(): can't cast data from an implementation-specific index type 0xcaca\n" "Trade::MeshData::mutableIndices(): can't cast data from an implementation-specific index type 0xcaca\n" "Trade::MeshData::indicesInto(): can't extract data out of an implementation-specific index type 0xcaca\n"); } void MeshDataTest::implementationSpecificVertexFormatWrongAccess() { CORRADE_SKIP_IF_NO_ASSERT(); VertexWithImplementationSpecificData vertexData[2]; Containers::StridedArrayView1D attribute{vertexData, &vertexData[0].thing, 2, sizeof(VertexWithImplementationSpecificData)}; MeshData data{MeshPrimitive::TriangleFan, DataFlag::Mutable, vertexData, { MeshAttributeData{MeshAttribute::Position, vertexFormatWrap(0xdead1), attribute}, MeshAttributeData{MeshAttribute::Tangent, vertexFormatWrap(0xdead2), attribute}, MeshAttributeData{MeshAttribute::Bitangent, vertexFormatWrap(0xdead3), attribute}, MeshAttributeData{MeshAttribute::Normal, vertexFormatWrap(0xdead4), attribute}, MeshAttributeData{MeshAttribute::TextureCoordinates, vertexFormatWrap(0xdead5), attribute}, MeshAttributeData{MeshAttribute::Color, vertexFormatWrap(0xdead6), attribute}, MeshAttributeData{MeshAttribute::JointIds, vertexFormatWrap(0xdead7), attribute, 2}, MeshAttributeData{MeshAttribute::Weights, vertexFormatWrap(0xdead8), attribute, 2}, MeshAttributeData{MeshAttribute::ObjectId, vertexFormatWrap(0xdead9), attribute}}}; std::ostringstream out; Error redirectError{&out}; data.attribute(MeshAttribute::Position); data.attribute(MeshAttribute::Normal); data.attribute(MeshAttribute::TextureCoordinates); data.attribute(MeshAttribute::Color); data.mutableAttribute(MeshAttribute::Position); data.mutableAttribute(MeshAttribute::Normal); data.mutableAttribute(MeshAttribute::TextureCoordinates); data.mutableAttribute(MeshAttribute::Color); data.positions2DAsArray(); data.positions3DAsArray(); data.tangentsAsArray(); data.bitangentSignsAsArray(); data.bitangentsAsArray(); data.normalsAsArray(); data.textureCoordinates2DAsArray(); data.colorsAsArray(); data.jointIdsAsArray(); data.weightsAsArray(); data.objectIdsAsArray(); CORRADE_COMPARE(out.str(), "Trade::MeshData::attribute(): can't cast data from an implementation-specific vertex format 0xdead1\n" "Trade::MeshData::attribute(): can't cast data from an implementation-specific vertex format 0xdead4\n" "Trade::MeshData::attribute(): can't cast data from an implementation-specific vertex format 0xdead5\n" "Trade::MeshData::attribute(): can't cast data from an implementation-specific vertex format 0xdead6\n" "Trade::MeshData::mutableAttribute(): can't cast data from an implementation-specific vertex format 0xdead1\n" "Trade::MeshData::mutableAttribute(): can't cast data from an implementation-specific vertex format 0xdead4\n" "Trade::MeshData::mutableAttribute(): can't cast data from an implementation-specific vertex format 0xdead5\n" "Trade::MeshData::mutableAttribute(): can't cast data from an implementation-specific vertex format 0xdead6\n" "Trade::MeshData::positions2DInto(): can't extract data out of an implementation-specific vertex format 0xdead1\n" "Trade::MeshData::positions3DInto(): can't extract data out of an implementation-specific vertex format 0xdead1\n" "Trade::MeshData::tangentsInto(): can't extract data out of an implementation-specific vertex format 0xdead2\n" "Trade::MeshData::bitangentSignsInto(): can't extract data out of an implementation-specific vertex format 0xdead2\n" "Trade::MeshData::bitangentsInto(): can't extract data out of an implementation-specific vertex format 0xdead3\n" "Trade::MeshData::normalsInto(): can't extract data out of an implementation-specific vertex format 0xdead4\n" "Trade::MeshData::textureCoordinatesInto(): can't extract data out of an implementation-specific vertex format 0xdead5\n" "Trade::MeshData::colorsInto(): can't extract data out of an implementation-specific vertex format 0xdead6\n" "Trade::MeshData::jointIdsInto(): can't extract data out of an implementation-specific vertex format 0xdead7\n" "Trade::MeshData::weightsInto(): can't extract data out of an implementation-specific vertex format 0xdead8\n" "Trade::MeshData::objectIdsInto(): can't extract data out of an implementation-specific vertex format 0xdead9\n"); } void MeshDataTest::mutableAccessNotAllowed() { CORRADE_SKIP_IF_NO_ASSERT(); const UnsignedShort indexData[3]{}; const Vector2 vertexData[2]{}; MeshIndexData indices{indexData}; MeshAttributeData positions{MeshAttribute::Position, Containers::arrayView(vertexData)}; MeshData data{MeshPrimitive::Triangles, {}, indexData, indices, {}, vertexData, {positions}}; CORRADE_COMPARE(data.indexDataFlags(), DataFlags{}); CORRADE_COMPARE(data.vertexDataFlags(), DataFlags{}); std::ostringstream out; Error redirectError{&out}; data.mutableIndexData(); data.mutableVertexData(); data.mutableIndices(); data.mutableIndices(); data.mutableAttribute(0); data.mutableAttribute(0); data.mutableAttribute(0); data.mutableAttribute(MeshAttribute::Position); data.mutableAttribute(MeshAttribute::Position); data.mutableAttribute(MeshAttribute::Position); CORRADE_COMPARE(out.str(), "Trade::MeshData::mutableIndexData(): index data not mutable\n" "Trade::MeshData::mutableVertexData(): vertex data not mutable\n" "Trade::MeshData::mutableIndices(): index data not mutable\n" "Trade::MeshData::mutableIndices(): index data not mutable\n" "Trade::MeshData::mutableAttribute(): vertex data not mutable\n" "Trade::MeshData::mutableAttribute(): vertex data not mutable\n" "Trade::MeshData::mutableAttribute(): vertex data not mutable\n" "Trade::MeshData::mutableAttribute(): vertex data not mutable\n" "Trade::MeshData::mutableAttribute(): vertex data not mutable\n" "Trade::MeshData::mutableAttribute(): vertex data not mutable\n"); } void MeshDataTest::indicesNotIndexed() { CORRADE_SKIP_IF_NO_ASSERT(); MeshData data{MeshPrimitive::Triangles, 37}; std::ostringstream out; Error redirectError{&out}; data.indexCount(); data.indexType(); data.indexOffset(); data.indexStride(); data.indices(); data.mutableIndices(); data.indicesAsArray(); UnsignedInt a[1]; data.indicesInto(a); CORRADE_COMPARE(out.str(), "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" "Trade::MeshData::indicesInto(): the mesh is not indexed\n"); } void MeshDataTest::indicesWrongType() { CORRADE_SKIP_IF_NO_ASSERT(); Containers::Array indexData{sizeof(UnsignedShort)}; auto indexView = Containers::arrayCast(indexData); indexView[0] = 57616; MeshData data{MeshPrimitive::Points, std::move(indexData), MeshIndexData{indexView}, 57617}; std::ostringstream out; Error redirectError{&out}; data.indices(); data.mutableIndices(); CORRADE_COMPARE(out.str(), "Trade::MeshData::indices(): indices are MeshIndexType::UnsignedShort but requested MeshIndexType::UnsignedByte\n" "Trade::MeshData::mutableIndices(): indices are MeshIndexType::UnsignedShort but requested MeshIndexType::UnsignedByte\n"); } void MeshDataTest::attributeNotFound() { CORRADE_SKIP_IF_NO_ASSERT(); MeshData data{MeshPrimitive::Points, nullptr, { MeshAttributeData{MeshAttribute::Color, VertexFormat::Vector3, nullptr}, MeshAttributeData{MeshAttribute::Weights, VertexFormat::UnsignedByteNormalized, nullptr, 3}, MeshAttributeData{MeshAttribute::JointIds, VertexFormat::UnsignedByte, nullptr, 3}, MeshAttributeData{MeshAttribute::Color, VertexFormat::Vector4, nullptr}, MeshAttributeData{MeshAttribute::Weights, VertexFormat::Float, nullptr, 6}, MeshAttributeData{MeshAttribute::JointIds, VertexFormat::UnsignedShort, nullptr, 6} }}; /* This is fine */ CORRADE_COMPARE(data.attributeCount(MeshAttribute::Position), 0); CORRADE_COMPARE(data.findAttributeId(MeshAttribute::Position), Containers::NullOpt); CORRADE_COMPARE(data.findAttributeId(MeshAttribute::Color, 2), Containers::NullOpt); std::ostringstream out; Error redirectError{&out}; data.attributeData(6); data.attributeName(6); data.attributeId(6); data.attributeFormat(6); data.attributeOffset(6); data.attributeStride(6); data.attributeArraySize(6); data.attribute(6); data.attribute(6); data.attribute(6); data.mutableAttribute(6); data.mutableAttribute(6); data.mutableAttribute(6); data.attributeId(MeshAttribute::Position); data.attributeId(MeshAttribute::Color, 2); data.attributeFormat(MeshAttribute::Position); data.attributeFormat(MeshAttribute::Color, 2); data.attributeOffset(MeshAttribute::Position); data.attributeOffset(MeshAttribute::Color, 2); data.attributeStride(MeshAttribute::Position); data.attributeStride(MeshAttribute::Color, 2); data.attributeArraySize(MeshAttribute::Position); data.attributeArraySize(MeshAttribute::Color, 2); data.attribute(MeshAttribute::Position); data.attribute(MeshAttribute::Color, 2); data.attribute(MeshAttribute::Position); data.attribute(MeshAttribute::Color, 2); data.attribute(MeshAttribute::Position); data.attribute(MeshAttribute::Color, 2); data.mutableAttribute(MeshAttribute::Position); data.mutableAttribute(MeshAttribute::Color, 2); data.mutableAttribute(MeshAttribute::Position); data.mutableAttribute(MeshAttribute::Color, 2); data.mutableAttribute(MeshAttribute::Position); data.mutableAttribute(MeshAttribute::Color, 2); data.positions2DAsArray(); data.positions3DAsArray(); data.tangentsAsArray(); data.bitangentSignsAsArray(); data.bitangentsAsArray(); data.normalsAsArray(); data.textureCoordinates2DAsArray(); data.colorsAsArray(2); /* jointIdsAsArray() and weightsAsArray() have their own assert in order to fetch array size, have to test also Into() for these */ data.jointIdsAsArray(2); data.jointIdsInto(nullptr, 2); data.weightsAsArray(2); data.weightsInto(nullptr, 2); data.objectIdsAsArray(); CORRADE_COMPARE(out.str(), "Trade::MeshData::attributeData(): index 6 out of range for 6 attributes\n" "Trade::MeshData::attributeName(): index 6 out of range for 6 attributes\n" "Trade::MeshData::attributeId(): index 6 out of range for 6 attributes\n" "Trade::MeshData::attributeFormat(): index 6 out of range for 6 attributes\n" "Trade::MeshData::attributeOffset(): index 6 out of range for 6 attributes\n" "Trade::MeshData::attributeStride(): index 6 out of range for 6 attributes\n" "Trade::MeshData::attributeArraySize(): index 6 out of range for 6 attributes\n" "Trade::MeshData::attribute(): index 6 out of range for 6 attributes\n" "Trade::MeshData::attribute(): index 6 out of range for 6 attributes\n" "Trade::MeshData::attribute(): index 6 out of range for 6 attributes\n" "Trade::MeshData::mutableAttribute(): index 6 out of range for 6 attributes\n" "Trade::MeshData::mutableAttribute(): index 6 out of range for 6 attributes\n" "Trade::MeshData::mutableAttribute(): index 6 out of range for 6 attributes\n" "Trade::MeshData::attributeId(): index 0 out of range for 0 Trade::MeshAttribute::Position attributes\n" "Trade::MeshData::attributeId(): index 2 out of range for 2 Trade::MeshAttribute::Color attributes\n" "Trade::MeshData::attributeFormat(): index 0 out of range for 0 Trade::MeshAttribute::Position attributes\n" "Trade::MeshData::attributeFormat(): index 2 out of range for 2 Trade::MeshAttribute::Color attributes\n" "Trade::MeshData::attributeOffset(): index 0 out of range for 0 Trade::MeshAttribute::Position attributes\n" "Trade::MeshData::attributeOffset(): index 2 out of range for 2 Trade::MeshAttribute::Color attributes\n" "Trade::MeshData::attributeStride(): index 0 out of range for 0 Trade::MeshAttribute::Position attributes\n" "Trade::MeshData::attributeStride(): index 2 out of range for 2 Trade::MeshAttribute::Color attributes\n" "Trade::MeshData::attributeArraySize(): index 0 out of range for 0 Trade::MeshAttribute::Position attributes\n" "Trade::MeshData::attributeArraySize(): index 2 out of range for 2 Trade::MeshAttribute::Color attributes\n" "Trade::MeshData::attribute(): index 0 out of range for 0 Trade::MeshAttribute::Position attributes\n" "Trade::MeshData::attribute(): index 2 out of range for 2 Trade::MeshAttribute::Color attributes\n" "Trade::MeshData::attribute(): index 0 out of range for 0 Trade::MeshAttribute::Position attributes\n" "Trade::MeshData::attribute(): index 2 out of range for 2 Trade::MeshAttribute::Color attributes\n" "Trade::MeshData::attribute(): index 0 out of range for 0 Trade::MeshAttribute::Position attributes\n" "Trade::MeshData::attribute(): index 2 out of range for 2 Trade::MeshAttribute::Color attributes\n" "Trade::MeshData::mutableAttribute(): index 0 out of range for 0 Trade::MeshAttribute::Position attributes\n" "Trade::MeshData::mutableAttribute(): index 2 out of range for 2 Trade::MeshAttribute::Color attributes\n" "Trade::MeshData::mutableAttribute(): index 0 out of range for 0 Trade::MeshAttribute::Position attributes\n" "Trade::MeshData::mutableAttribute(): index 2 out of range for 2 Trade::MeshAttribute::Color attributes\n" "Trade::MeshData::mutableAttribute(): index 0 out of range for 0 Trade::MeshAttribute::Position attributes\n" "Trade::MeshData::mutableAttribute(): index 2 out of range for 2 Trade::MeshAttribute::Color attributes\n" "Trade::MeshData::positions2DInto(): index 0 out of range for 0 position attributes\n" "Trade::MeshData::positions3DInto(): index 0 out of range for 0 position attributes\n" "Trade::MeshData::tangentsInto(): index 0 out of range for 0 tangent attributes\n" "Trade::MeshData::bitangentSignsInto(): index 0 out of range for 0 tangent attributes\n" "Trade::MeshData::bitangentsInto(): index 0 out of range for 0 bitangent attributes\n" "Trade::MeshData::normalsInto(): index 0 out of range for 0 normal attributes\n" "Trade::MeshData::textureCoordinates2DInto(): index 0 out of range for 0 texture coordinate attributes\n" "Trade::MeshData::colorsInto(): index 2 out of range for 2 color attributes\n" "Trade::MeshData::jointIdsAsArray(): index 2 out of range for 2 joint ID attributes\n" "Trade::MeshData::jointIdsInto(): index 2 out of range for 2 joint ID attributes\n" "Trade::MeshData::weightsAsArray(): index 2 out of range for 2 weight attributes\n" "Trade::MeshData::weightsInto(): index 2 out of range for 2 weight attributes\n" "Trade::MeshData::objectIdsInto(): index 0 out of range for 0 object ID attributes\n"); } void MeshDataTest::attributeWrongType() { CORRADE_SKIP_IF_NO_ASSERT(); MeshAttributeData positions{MeshAttribute::Position, VertexFormat::Vector3, nullptr}; MeshData data{MeshPrimitive::Points, nullptr, {positions}}; std::ostringstream out; Error redirectError{&out}; data.attribute(MeshAttribute::Position); data.attribute(MeshAttribute::Position); data.mutableAttribute(MeshAttribute::Position); data.mutableAttribute(MeshAttribute::Position); CORRADE_COMPARE(out.str(), "Trade::MeshData::attribute(): Trade::MeshAttribute::Position is VertexFormat::Vector3 but requested a type equivalent to VertexFormat::Vector4\n" "Trade::MeshData::attribute(): Trade::MeshAttribute::Position is VertexFormat::Vector3 but requested a type equivalent to VertexFormat::Vector4\n" "Trade::MeshData::mutableAttribute(): Trade::MeshAttribute::Position is VertexFormat::Vector3 but requested a type equivalent to VertexFormat::Vector4\n" "Trade::MeshData::mutableAttribute(): Trade::MeshAttribute::Position is VertexFormat::Vector3 but requested a type equivalent to VertexFormat::Vector4\n"); } void MeshDataTest::attributeWrongArrayAccess() { CORRADE_SKIP_IF_NO_ASSERT(); Vector2 vertexData[3*4]{ {1.0f, 2.0f}, {3.0f, 4.0f}, {5.0f, 6.0f}, {7.0f, 8.0f}, {1.1f, 2.2f}, {3.3f, 4.4f}, {5.5f, 6.6f}, {7.7f, 8.8f}, {0.1f, 0.2f}, {0.3f, 0.4f}, {0.5f, 0.6f}, {0.7f, 0.8f}, }; Containers::StridedArrayView1D positions{vertexData, 3, 4*sizeof(Vector2)}; Containers::StridedArrayView2D positions2D{vertexData, {3, 4}}; MeshData data{MeshPrimitive::TriangleFan, DataFlag::Mutable, vertexData, { MeshAttributeData{MeshAttribute::Position, positions}, MeshAttributeData{meshAttributeCustom(35), positions2D} }}; std::ostringstream out; Error redirectError{&out}; data.attribute(0); data.attribute(1); data.mutableAttribute(0); data.mutableAttribute(1); data.attribute(MeshAttribute::Position); data.attribute(meshAttributeCustom(35)); data.mutableAttribute(MeshAttribute::Position); data.mutableAttribute(meshAttributeCustom(35)); CORRADE_COMPARE(out.str(), "Trade::MeshData::attribute(): Trade::MeshAttribute::Position is not an array attribute, can't use T[] to access it\n" "Trade::MeshData::attribute(): Trade::MeshAttribute::Custom(35) is an array attribute, use T[] to access it\n" "Trade::MeshData::mutableAttribute(): Trade::MeshAttribute::Position is not an array attribute, can't use T[] to access it\n" "Trade::MeshData::mutableAttribute(): Trade::MeshAttribute::Custom(35) is an array attribute, use T[] to access it\n" "Trade::MeshData::attribute(): Trade::MeshAttribute::Position is not an array attribute, can't use T[] to access it\n" "Trade::MeshData::attribute(): Trade::MeshAttribute::Custom(35) is an array attribute, use T[] to access it\n" "Trade::MeshData::mutableAttribute(): Trade::MeshAttribute::Position is not an array attribute, can't use T[] to access it\n" "Trade::MeshData::mutableAttribute(): Trade::MeshAttribute::Custom(35) is an array attribute, use T[] to access it\n"); } void MeshDataTest::releaseIndexData() { Containers::Array indexData{23}; auto indexView = Containers::arrayCast(indexData.slice(6, 12)); MeshData data{MeshPrimitive::TriangleStrip, std::move(indexData), MeshIndexData{indexView}, 10}; CORRADE_VERIFY(data.isIndexed()); CORRADE_COMPARE(data.indexCount(), 3); CORRADE_COMPARE(data.indexOffset(), 6); Containers::Array released = data.releaseIndexData(); CORRADE_COMPARE(static_cast(released.data() + 6), indexView.data()); /* This is not null as we still need the value for calculating offsets */ CORRADE_COMPARE(static_cast(data.indexData()), released.data()); CORRADE_COMPARE(data.indexData().size(), 0); CORRADE_VERIFY(data.isIndexed()); CORRADE_COMPARE(data.indexCount(), 0); CORRADE_COMPARE(data.indexType(), MeshIndexType::UnsignedShort); CORRADE_COMPARE(data.indexOffset(), 6); } void MeshDataTest::releaseAttributeData() { Containers::Array vertexData{16}; auto vertexView = Containers::arrayCast(vertexData); MeshAttributeData positions{MeshAttribute::Position, vertexView}; MeshData data{MeshPrimitive::LineLoop, std::move(vertexData), {positions, positions}}; CORRADE_COMPARE(data.attributeCount(), 2); Containers::Array released = data.releaseAttributeData(); CORRADE_COMPARE(released.size(), 2); CORRADE_COMPARE(static_cast(released[0].data().data()), vertexView.data()); CORRADE_COMPARE(released[0].data().size(), 2); /* Unlike the other two, this is null as we don't need the value for calculating anything */ CORRADE_COMPARE(static_cast(data.attributeData()), nullptr); CORRADE_COMPARE(data.attributeCount(), 0); CORRADE_COMPARE(static_cast(data.vertexData()), vertexView); CORRADE_COMPARE(data.vertexCount(), 2); } void MeshDataTest::releaseVertexData() { Containers::Array vertexData{80}; auto vertexView = Containers::arrayCast(vertexData.slice(48, 72)); MeshAttributeData positions{MeshAttribute::Position, vertexView}; MeshData data{MeshPrimitive::LineLoop, std::move(vertexData), {positions, positions}}; CORRADE_COMPARE(data.attributeCount(), 2); CORRADE_COMPARE(data.vertexCount(), 3); CORRADE_COMPARE(data.attributeOffset(0), 48); Containers::Array released = data.releaseVertexData(); CORRADE_VERIFY(data.attributeData()); CORRADE_COMPARE(data.attributeCount(), 2); CORRADE_COMPARE(static_cast(static_cast(data.attribute(0).data())), vertexView.data()); CORRADE_COMPARE(static_cast(static_cast(data.mutableAttribute(0).data())), vertexView.data()); /* Returned views should be patched to have zero size (but not the direct access, there it stays as it's an internal API really) */ CORRADE_COMPARE(data.attribute(0).size()[0], 0); CORRADE_COMPARE(data.mutableAttribute(0).size()[0], 0); CORRADE_COMPARE(data.attributeData()[0].data().size(), 3); CORRADE_COMPARE(static_cast(released.data() + 48), vertexView.data()); /* This is not null as we still need the value for calculating offsets */ CORRADE_COMPARE(static_cast(data.vertexData()), released.data()); CORRADE_COMPARE(data.vertexCount(), 0); CORRADE_COMPARE(data.attributeOffset(0), 48); } }}}} CORRADE_TEST_MAIN(Magnum::Trade::Test::MeshDataTest)