Browse Source

Trade: a redesigned MaterialData class.

Next step is reworking PhongMaterialData on top of this one and
deprecating AbstractMaterialData.
pull/459/head
Vladimír Vondruš 6 years ago
parent
commit
14d260a3ea
  1. 6
      doc/changelog.dox
  2. 5
      src/Magnum/Trade/CMakeLists.txt
  3. 49
      src/Magnum/Trade/Implementation/materialAttributeProperties.hpp
  4. 314
      src/Magnum/Trade/MaterialData.cpp
  5. 779
      src/Magnum/Trade/MaterialData.h
  6. 1
      src/Magnum/Trade/Test/CMakeLists.txt
  7. 732
      src/Magnum/Trade/Test/MaterialDataTest.cpp
  8. 4
      src/Magnum/Trade/Trade.h

6
doc/changelog.dox

@ -72,6 +72,12 @@ See also:
- Added @ref SceneGraph::Object::move()
@subsubsection changelog-latest-new-trade Trade library
- A new, redesigned @ref Trade::MaterialData class allowing to store custom
material attributes as well as more material types together in a single
instance. See [mosra/magnum#459](https://github.com/mosra/magnum/pull/459).
@subsection changelog-latest-changes Changes and improvements
@subsubsection changelog-latest-changes-gl GL library

5
src/Magnum/Trade/CMakeLists.txt

@ -42,6 +42,7 @@ set(MagnumTrade_GracefulAssert_SRCS
AnimationData.cpp
CameraData.cpp
ImageData.cpp
MaterialData.cpp
MeshData.cpp
ObjectData2D.cpp
ObjectData3D.cpp
@ -58,6 +59,7 @@ set(MagnumTrade_HEADERS
Data.h
ImageData.h
LightData.h
MaterialData.h
MeshData.h
MeshObjectData2D.h
MeshObjectData3D.h
@ -72,7 +74,8 @@ set(MagnumTrade_HEADERS
set(MagnumTrade_PRIVATE_HEADERS
Implementation/arrayUtilities.h
Implementation/converterUtilities.h)
Implementation/converterUtilities.h
Implementation/materialAttributeProperties.hpp)
if(MAGNUM_BUILD_DEPRECATED)
list(APPEND MagnumTrade_GracefulAssert_SRCS

49
src/Magnum/Trade/Implementation/materialAttributeProperties.hpp

@ -0,0 +1,49 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
/* See Magnum/Trade/MaterialData.cpp and Magnum/Trade/Test/MaterialDataTest.cpp */
#ifdef _c
_c(AlphaMask,Float)
_ct(AlphaBlend,Bool,bool)
_ct(DoubleSided,Bool,bool)
_c(AmbientColor,Vector4)
_c(AmbientTexture,UnsignedInt)
_c(AmbientTextureMatrix,Matrix3x3)
_c(AmbientTextureCoordinates,UnsignedInt)
_c(DiffuseColor,Vector4)
_c(DiffuseTexture,UnsignedInt)
_c(DiffuseTextureMatrix,Matrix3x3)
_c(DiffuseTextureCoordinates,UnsignedInt)
_c(SpecularColor,Vector4)
_c(SpecularTexture,UnsignedInt)
_c(SpecularTextureMatrix,Matrix3x3)
_c(SpecularTextureCoordinates,UnsignedInt)
_c(Shininess,Float)
_c(NormalTexture,UnsignedInt)
_c(NormalTextureMatrix,Matrix3x3)
_c(NormalTextureCoordinates,UnsignedInt)
_c(TextureMatrix,Matrix3x3)
_c(TextureCoordinates,UnsignedInt)
#endif

314
src/Magnum/Trade/MaterialData.cpp

@ -0,0 +1,314 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include "MaterialData.h"
#include <cstring>
#include <algorithm>
#include "Magnum/Math/Vector4.h"
#include "Magnum/Math/Matrix.h"
#include "Magnum/Trade/Implementation/arrayUtilities.h"
namespace Magnum { namespace Trade {
namespace {
using namespace Containers::Literals;
#ifndef DOXYGEN_GENERATING_OUTPUT /* It gets *really* confused */
constexpr struct {
Containers::StringView name;
MaterialAttributeType type;
UnsignedByte size;
} AttributeMap[]{
#define _c(name, type) {#name ## _s, MaterialAttributeType::type, sizeof(type)},
#define _ct(name, typeName, type) {#name ## _s, MaterialAttributeType::typeName, sizeof(type)},
#include "Magnum/Trade/Implementation/materialAttributeProperties.hpp"
#undef _c
#undef _ct
};
#endif
}
std::size_t materialAttributeTypeSize(const MaterialAttributeType type) {
switch(type) {
case MaterialAttributeType::Bool:
return 1;
case MaterialAttributeType::Float:
case MaterialAttributeType::Deg:
case MaterialAttributeType::Rad:
case MaterialAttributeType::UnsignedInt:
case MaterialAttributeType::Int:
return 4;
case MaterialAttributeType::Vector2:
case MaterialAttributeType::Vector2ui:
case MaterialAttributeType::Vector2i:
return 8;
case MaterialAttributeType::Vector3:
case MaterialAttributeType::Vector3ui:
case MaterialAttributeType::Vector3i:
return 12;
case MaterialAttributeType::Vector4:
case MaterialAttributeType::Vector4ui:
case MaterialAttributeType::Vector4i:
case MaterialAttributeType::Matrix2x2:
return 16;
case MaterialAttributeType::Matrix2x3:
case MaterialAttributeType::Matrix3x2:
return 24;
case MaterialAttributeType::Matrix2x4:
case MaterialAttributeType::Matrix4x2:
return 32;
case MaterialAttributeType::Matrix3x3:
return 36;
case MaterialAttributeType::Matrix3x4:
case MaterialAttributeType::Matrix4x3:
return 48;
}
CORRADE_ASSERT_UNREACHABLE("Trade::materialAttributeTypeSize(): invalid type" << type, {});
}
MaterialAttributeData::MaterialAttributeData(const Containers::StringView name, const MaterialAttributeType type, const std::size_t size, const void* const value) noexcept: _data{} /* zero-initialized */ {
/* The 2 extra bytes are for a null byte after the name and a type */
CORRADE_ASSERT(name.size() + size + 2 <= Implementation::MaterialAttributeDataSize,
"Trade::MaterialAttributeData: name" << name << "too long, expected at most" << Implementation::MaterialAttributeDataSize - size - 2 << "bytes for" << type << "but got" << name.size(), );
_data.type = type;
std::memcpy(_data.data + 1, name.data(), name.size());
std::memcpy(_data.data + Implementation::MaterialAttributeDataSize - size, value, size);
}
MaterialAttributeData::MaterialAttributeData(const Containers::StringView name, const MaterialAttributeType type, const void* const value) noexcept: MaterialAttributeData{name, type, materialAttributeTypeSize(type), value} {}
MaterialAttributeData::MaterialAttributeData(const MaterialAttribute name, const MaterialAttributeType type, const void* const value) noexcept {
CORRADE_ASSERT(UnsignedInt(name) - 1 < Containers::arraySize(AttributeMap),
"Trade::MaterialAttributeData: invalid name" << name, );
CORRADE_ASSERT(AttributeMap[UnsignedInt(name) - 1].type == type,
"Trade::MaterialAttributeData: expected" << AttributeMap[UnsignedInt(name) - 1].type << "for" << name << "but got" << type, );
_data.type = type;
std::memcpy(_data.data + 1, AttributeMap[UnsignedInt(name) - 1].name.data(), AttributeMap[UnsignedInt(name) - 1].name.size());
std::memcpy(_data.data + Implementation::MaterialAttributeDataSize - AttributeMap[UnsignedInt(name) - 1].size, value, AttributeMap[UnsignedInt(name) - 1].size);
}
const void* MaterialAttributeData::value() const {
return _data.data + Implementation::MaterialAttributeDataSize - materialAttributeTypeSize(_data.type);
}
MaterialData::MaterialData(Containers::Array<MaterialAttributeData>&& data, const void* const importerState) noexcept: _data{std::move(data)}, _importerState{importerState} {
#ifndef CORRADE_NO_ASSERT
/* Not checking what's already done in MaterialAttributeData constructor.
Done before sorting so the index refers to the actual input index. */
for(std::size_t i = 0; i != _data.size(); ++i)
CORRADE_ASSERT(_data[i]._data.type != MaterialAttributeType{},
"Trade::MaterialData: attribute" << i << "doesn't specify anything", );
#endif
/* Check if attributes are sorted already. If not, sort them. Can't just
call std::sort() unconditionally because if the data would be immutable
(for example when acquiring release()d immutable data from another
instance) it could cause crashes. (I expected not, but apparently ASan
blows up on that.) */
if(_data.size() > 1) for(std::size_t i = 1; i != _data.size(); ++i) {
if(_data[i - 1].name() < _data[i].name()) continue;
std::sort(_data.begin(), _data.end(), [](const MaterialAttributeData& a, const MaterialAttributeData& b) {
/* Need to check here (instead of in the outer for loop) as we
exit the loop right after the sort and thus duplicates that
occur after the first non-sorted pair wouldn't get detected. */
CORRADE_ASSERT(a.name() != b.name(),
"Trade::MaterialData: duplicate attribute" << a.name(), false);
return a.name() < b.name();
});
break;
}
}
MaterialData::MaterialData(const std::initializer_list<MaterialAttributeData> data, const void* const importerState): MaterialData{Implementation::initializerListToArrayWithDefaultDeleter(data), importerState} {}
MaterialData::MaterialData(DataFlags, const Containers::ArrayView<const MaterialAttributeData> data, const void* const importerState) noexcept: _data{Containers::Array<MaterialAttributeData>{const_cast<MaterialAttributeData*>(data.data()), data.size(), [](MaterialAttributeData*, std::size_t){}}}, _importerState{importerState} {
#ifndef CORRADE_NO_ASSERT
/* Not checking what's already done in MaterialAttributeData constructor */
for(std::size_t i = 0; i != _data.size(); ++i)
CORRADE_ASSERT(_data[i]._data.type != MaterialAttributeType{},
"Trade::MaterialData: attribute" << i << "doesn't specify anything", );
if(_data.size() > 1) for(std::size_t i = 1; i != _data.size(); ++i) {
CORRADE_ASSERT(_data[i - 1].name() != _data[i].name(),
"Trade::MaterialData: duplicate attribute" << _data[i].name(), );
CORRADE_ASSERT(_data[i - 1].name() < _data[i].name(),
"Trade::MaterialData:" << _data[i].name() << "has to be sorted before" << _data[i - 1].name() << "if passing non-owned data", );
}
#endif
}
MaterialData::MaterialData(MaterialData&&) noexcept = default;
MaterialData::~MaterialData() = default;
MaterialData& MaterialData::operator=(MaterialData&&) noexcept = default;
Containers::StringView MaterialData::attributeString(const MaterialAttribute name) {
#ifndef CORRADE_NO_ASSERT
if(UnsignedInt(name) - 1 >= Containers::arraySize(AttributeMap))
return nullptr;
#endif
return AttributeMap[UnsignedInt(name) - 1].name;
}
UnsignedInt MaterialData::attributeFor(const Containers::StringView name) const {
const MaterialAttributeData* const found = std::lower_bound(_data.begin(), _data.end(), name, [](const MaterialAttributeData& a, const Containers::StringView& b) {
return a.name() < b;
});
if(found == _data.end() || found->name() != name) return ~UnsignedInt{};
return found - _data.begin();
}
bool MaterialData::hasAttribute(const Containers::StringView name) const {
return attributeFor(name) != ~UnsignedInt{};
}
bool MaterialData::hasAttribute(const MaterialAttribute name) const {
const Containers::StringView string = attributeString(name);
CORRADE_ASSERT(string.data(), "Trade::MaterialData::hasAttribute(): invalid name" << name, {});
return hasAttribute(string);
}
UnsignedInt MaterialData::attributeId(const Containers::StringView name) const {
const UnsignedInt id = attributeFor(name);
CORRADE_ASSERT(id != ~UnsignedInt{},
"Trade::MaterialData::attributeId(): attribute" << name << "not found", {});
return id;
}
UnsignedInt MaterialData::attributeId(const MaterialAttribute name) const {
const Containers::StringView string = attributeString(name);
CORRADE_ASSERT(string.data(), "Trade::MaterialData::attributeId(): invalid name" << name, {});
return attributeId(string);
}
Containers::StringView MaterialData::attributeName(const UnsignedInt id) const {
CORRADE_ASSERT(id < _data.size(),
"Trade::MaterialData::attributeName(): index" << id << "out of range for" << _data.size() << "attributes", {});
return _data[id]._data.data + 1;
}
MaterialAttributeType MaterialData::attributeType(const UnsignedInt id) const {
CORRADE_ASSERT(id < _data.size(),
"Trade::MaterialData::attributeType(): index" << id << "out of range for" << _data.size() << "attributes", {});
return _data[id]._data.type;
}
MaterialAttributeType MaterialData::attributeType(const Containers::StringView name) const {
const UnsignedInt id = attributeFor(name);
CORRADE_ASSERT(id != ~UnsignedInt{},
"Trade::MaterialData::attributeType(): attribute" << name << "not found", {});
return _data[id]._data.type;
}
MaterialAttributeType MaterialData::attributeType(const MaterialAttribute name) const {
const Containers::StringView string = attributeString(name);
CORRADE_ASSERT(string.data(), "Trade::MaterialData::attributeType(): invalid name" << name, {});
return attributeType(string);
}
const void* MaterialData::attribute(const UnsignedInt id) const {
CORRADE_ASSERT(id < _data.size(),
"Trade::MaterialData::attribute(): index" << id << "out of range for" << _data.size() << "attributes", {});
return _data[id].value();
}
const void* MaterialData::attribute(const Containers::StringView name) const {
const UnsignedInt id = attributeFor(name);
CORRADE_ASSERT(id != ~UnsignedInt{},
"Trade::MaterialData::attribute(): attribute" << name << "not found", {});
return _data[id].value();
}
const void* MaterialData::attribute(const MaterialAttribute name) const {
const Containers::StringView string = attributeString(name);
CORRADE_ASSERT(string.data(), "Trade::MaterialData::attribute(): invalid name" << name, {});
return attribute(string);
}
Containers::Array<MaterialAttributeData> MaterialData::release() {
return std::move(_data);
}
Debug& operator<<(Debug& debug, const MaterialAttribute value) {
debug << "Trade::MaterialAttribute" << Debug::nospace;
if(UnsignedInt(value) - 1 >= Containers::arraySize(AttributeMap))
return debug << "(" << Debug::nospace << reinterpret_cast<void*>(UnsignedInt(value)) << Debug::nospace << ")";
return debug << "::" << Debug::nospace << AttributeMap[UnsignedInt(value) - 1].name;
}
Debug& operator<<(Debug& debug, const MaterialAttributeType value) {
debug << "Trade::MaterialAttributeType" << Debug::nospace;
switch(value) {
/* LCOV_EXCL_START */
#define _c(value) case MaterialAttributeType::value: return debug << "::" #value;
_c(Bool)
_c(Float)
_c(Deg)
_c(Rad)
_c(UnsignedInt)
_c(Int)
_c(Vector2)
_c(Vector2ui)
_c(Vector2i)
_c(Vector3)
_c(Vector3ui)
_c(Vector3i)
_c(Vector4)
_c(Vector4ui)
_c(Vector4i)
_c(Matrix2x2)
_c(Matrix2x3)
_c(Matrix2x4)
_c(Matrix3x2)
_c(Matrix3x3)
_c(Matrix3x4)
_c(Matrix4x2)
_c(Matrix4x3)
#undef _c
/* LCOV_EXCL_STOP */
}
return debug << "(" << Debug::nospace << reinterpret_cast<void*>(UnsignedShort(value)) << Debug::nospace << ")";
}
}}

779
src/Magnum/Trade/MaterialData.h

@ -0,0 +1,779 @@
#ifndef Magnum_Trade_MaterialData_h
#define Magnum_Trade_MaterialData_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
/** @file
* @brief Class @ref Magnum::Trade::MaterialData, @ref Magnum::Trade::MaterialAttributeData, enum @ref Magnum::Trade::MaterialAttribute, @ref Magnum::Trade::MaterialAttributeType
* @m_since_latest
*/
#include <Corrade/Containers/Array.h>
#include <Corrade/Containers/StringView.h>
#include "Magnum/Magnum.h"
#include "Magnum/Math/RectangularMatrix.h"
#include "Magnum/Trade/Trade.h"
#include "Magnum/Trade/visibility.h"
namespace Magnum { namespace Trade {
/**
@brief Material attribute name
@m_since_latest
Convenience aliases to actual attribute name strings, in the same form and
capitalization --- so for example @ref MaterialAttribute::DoubleSided is an
alias for @cpp "DoubleSided" @ce. When this enum si used in
@ref MaterialAttributeData constructors, the data are additionally checked for
type compatibility. Other than that, there is no difference to the string
variants.
@see @ref MaterialAttributeData, @ref MaterialData
*/
enum class MaterialAttribute: UnsignedInt {
/* Zero used for an invalid value */
/**
* Alpha mask, @ref MaterialAttributeType::Float.
*
* If set together with @ref MaterialAttribute::AlphaBlend, blending is
* preferred, however renderers can fall back to alpha-masked rendering.
* Alpha values below this value are meant to be rendered as fully
* transparent and alpha values above this value as fully opaque.
*/
AlphaMask = 1,
/**
* Alpha blending, @ref MaterialAttributeType::Bool.
*
* If @cpp true @ce, the material is expected to be rendered with blending
* enabled and in correct depth order. If @cpp false @ce or not present,
* the material should be treated as opaque. If set together with
* @ref MaterialAttribute::AlphaMask, blending is preferred, however
* renderers can fall back to alpha-masked rendering.
*/
AlphaBlend,
/**
* Double sided, @ref MaterialAttributeType::Bool.
*
* If not present, the default value is @cpp false @ce.
*/
DoubleSided,
/**
* Ambient color for Phong materials, @ref MaterialAttributeType::Vector4.
*
* If @ref MaterialAttribute::AmbientTexture is present as well, these two
* are multiplied together.
*/
AmbientColor,
/**
* Ambient texture index for Phong materials,
* @ref MaterialAttributeType::UnsignedInt.
*
* If @ref MaterialAttribute::AmbientColor is present as well, these two
* are multiplied together.
*/
AmbientTexture,
/**
* Ambient texture transformation matrix for Phong materials,
* @ref MaterialAttributeType::Matrix3x3.
*
* Has a precedence over @ref MaterialAttribute::TextureMatrix if both are
* present.
*/
AmbientTextureMatrix,
/**
* Ambient texture coordinate set index for Phong materials,
* @ref MaterialAttributeType::UnsignedInt.
*
* Has a precedence over @ref MaterialAttribute::TextureCoordinates if both
* are present.
*/
AmbientTextureCoordinates,
/**
* Diffuse color for Phong materials, @ref MaterialAttributeType::Vector4.
*
* If @ref MaterialAttribute::DiffuseTexture is present as well, these two
* are multiplied together.
*/
DiffuseColor,
/**
* Diffuse texture index for Phong materials,
* @ref MaterialAttributeType::UnsignedInt.
*
* If @ref MaterialAttribute::DiffuseColor is present as well, these two
* are multiplied together.
*/
DiffuseTexture,
/**
* Diffuse texture transformation matrix for Phong materials,
* @ref MaterialAttributeType::Matrix3x3.
*
* Has a precedence over @ref MaterialAttribute::TextureMatrix if both are
* present.
*/
DiffuseTextureMatrix,
/**
* Diffuse texture coordinate set index for Phong materials,
* @ref MaterialAttributeType::UnsignedInt.
*
* Has a precedence over @ref MaterialAttribute::TextureCoordinates if both
* are present.
*/
DiffuseTextureCoordinates,
/**
* Specular color for Phong materials, @ref MaterialAttributeType::Vector4.
*
* If @ref MaterialAttribute::SpecularTexture is present as well, these two
* are multiplied together.
*/
SpecularColor,
/**
* Specular texture index for Phong materials,
* @ref MaterialAttributeType::UnsignedInt.
*
* If @ref MaterialAttribute::SpecularColor is present as well, these two
* are multiplied together.
*/
SpecularTexture,
/**
* Specular texture transformation matrix for Phong materials,
* @ref MaterialAttributeType::Matrix3x3.
*
* Has a precedence over @ref MaterialAttribute::TextureMatrix if both are
* present.
*/
SpecularTextureMatrix,
/**
* Specular texture coordinate set index for Phong materials,
* @ref MaterialAttributeType::UnsignedInt.
*
* Has a precedence over @ref MaterialAttribute::TextureCoordinates if both
* are present.
*/
SpecularTextureCoordinates,
/**
* Shininess value for Phong materials, @ref MaterialAttributeType::Float.
*/
Shininess,
/**
* Tangent-space normal map texture index,
* @ref MaterialAttributeType::UnsignedInt.
*/
NormalTexture,
/**
* Normal texture transformation matrix,
* @ref MaterialAttributeType::Matrix3x3.
*
* Has a precedence over @ref MaterialAttribute::TextureMatrix if both are
* present.
*/
NormalTextureMatrix,
/**
* Normal texture coordinate set index,
* @ref MaterialAttributeType::UnsignedInt.
*
* Has a precedence over @ref MaterialAttribute::TextureCoordinates if both
* are present.
*/
NormalTextureCoordinates,
/**
* Common texture transformation matrix for all textures,
* @ref MaterialAttributeType::Matrix3x3.
*
* @ref MaterialAttribute::AmbientTextureMatrix /
* @ref MaterialAttribute::DiffuseTextureMatrix /
* @ref MaterialAttribute::SpecularTextureMatrix /
* @ref MaterialAttribute::NormalTextureMatrix have a precedence over this
* attribute for given texture, if present.
*/
TextureMatrix,
/**
* Common texture coordinate set index for all textures,
* @ref MaterialAttributeType::UnsignedInt.
*
* @ref MaterialAttribute::AmbientTextureCoordinates /
* @ref MaterialAttribute::DiffuseTextureCoordinates /
* @ref MaterialAttribute::SpecularTextureCoordinates /
* @ref MaterialAttribute::NormalTextureCoordinates have a precedence over
* this attribute for given texture, if present.
*/
TextureCoordinates,
};
/**
@debugoperatorenum{MaterialAttribute}
@m_since_latest
*/
MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, MaterialAttribute value);
/**
@brief Material attribute type
@m_since_latest
See @ref MaterialData for more information.
@see @ref MaterialAttribute, @ref MaterialAttributeData,
@ref materialAttributeTypeSize()
*/
enum class MaterialAttributeType: UnsignedByte {
/* Zero used for an invalid value */
Bool = 1, /**< @cpp bool @ce <b></b> */
Float, /**< @ref Magnum::Float "Float" */
Deg, /**< @ref Magnum::Deg "Deg" */
Rad, /**< @ref Magnum::Rad "Rad" */
UnsignedInt, /**< @ref Magnum::UnsignedInt "UnsignedInt" */
Int, /**< @ref Magnum::Int "Int" */
Vector2, /**< @ref Magnum::Vector2 "Vector2" */
Vector2ui, /**< @ref Magnum::Vector2ui "Vector2ui" */
Vector2i, /**< @ref Magnum::Vector2i "Vector2i" */
Vector3, /**< @ref Magnum::Vector3 "Vector3" */
Vector3ui, /**< @ref Magnum::Vector3ui "Vector3ui" */
Vector3i, /**< @ref Magnum::Vector3i "Vector3i" */
Vector4, /**< @ref Magnum::Vector4 "Vector4" */
Vector4ui, /**< @ref Magnum::Vector4ui "Vector4ui" */
Vector4i, /**< @ref Magnum::Vector4i "Vector4i" */
Matrix2x2, /**< @ref Magnum::Matrix2x2 "Matrix2x2" */
Matrix2x3, /**< @ref Magnum::Matrix2x3 "Matrix2x3" */
Matrix2x4, /**< @ref Magnum::Matrix2x4 "Matrix2x4" */
Matrix3x2, /**< @ref Magnum::Matrix3x2 "Matrix3x2" */
Matrix3x3, /**< @ref Magnum::Matrix3x3 "Matrix3x3" */
Matrix3x4, /**< @ref Magnum::Matrix3x4 "Matrix3x4" */
Matrix4x2, /**< @ref Magnum::Matrix4x2 "Matrix4x2" */
Matrix4x3, /**< @ref Magnum::Matrix4x3 "Matrix4x3" */
/* Matrix4x4 not present */
};
/**
@brief Byte size of a material attribute type
@m_since_latest
*/
MAGNUM_TRADE_EXPORT std::size_t materialAttributeTypeSize(MaterialAttributeType type);
/**
@debugoperatorenum{MaterialAttributeType}
@m_since_latest
*/
MAGNUM_TRADE_EXPORT Debug& operator<<(Debug& debug, MaterialAttributeType value);
namespace Implementation {
template<class> struct MaterialAttributeTypeFor;
enum: std::size_t { MaterialAttributeDataSize = 64 };
}
/**
@brief Material attribute data
@m_since_latest
See @ref MaterialData for more information.
*/
class MAGNUM_TRADE_EXPORT MaterialAttributeData {
public:
/**
* @brief Default constructor
*
* Leaves contents at unspecified values. Provided as a convenience for
* initialization of the attribute array for @ref MaterialData,
* expected to be replaced with concrete values later.
*/
constexpr explicit MaterialAttributeData() noexcept: _data{} {}
/**
* @brief Construct with a string name
* @param name Attribute name
* @param value Attribute value
*
* The @p name together with @p value is expected to fit into 62 bytes.
* @ref MaterialAttributeType is inferred from the type passed.
*
* This function is useful in @cpp constexpr @ce contexts and for
* creating custom material attributes. For known attributes prefer to
* use @ref MaterialAttributeData(MaterialAttribute, const T&) if you
* don't need @cpp constexpr @ce, as it additionally checks that given
* attribute has the expected type.
*/
template<class T> constexpr /*implicit*/ MaterialAttributeData(Containers::StringView name, const T& value) noexcept;
#ifndef DOXYGEN_GENERATING_OUTPUT
/* "Sure can't be constexpr" overload to avoid going through the
*insane* overload puzzle when not needed */
template<class T> /*implicit*/ MaterialAttributeData(const char* name, const T& value) noexcept: MaterialAttributeData{name, Implementation::MaterialAttributeTypeFor<T>::type(), sizeof(T), &value} {}
#endif
/**
* @brief Construct with a predefined name
* @param name Attribute name
* @param value Attribute value
*
* Compared to @ref MaterialAttributeData(Containers::StringView, const T&)
* checks that the attribute is in expected type. The
* @ref MaterialAttribute gets converted to a corresponding string
* name. Apart from the type check, the following two instances are
* equivalent:
*
* @snippet MagnumTrade.cpp MaterialAttributeData-name
*/
template<class T> /*implicit*/ MaterialAttributeData(MaterialAttribute name, const T& value) noexcept: MaterialAttributeData{name, Implementation::MaterialAttributeTypeFor<T>::type(), &value} {}
/**
* @brief Construct from a type-erased value
* @param name Attribute name
* @param type Attribute type
* @param value Type-erased value
*
* Copies a number of bytes according to @ref materialAttributeTypeSize()
* from @p value. The @p name together with @p value is expected to fit
* into 62 bytes.
*/
/*implicit*/ MaterialAttributeData(Containers::StringView name, MaterialAttributeType type, const void* value) noexcept;
/**
* @brief Construct with a predefined name
* @param name Attribute name
* @param type Attribute type
* @param value Attribute value
*
* Compared to @ref MaterialAttributeData(Containers::StringView, MaterialAttributeType, const void*)
* checks that the attribute is in expected type. The
* @ref MaterialAttribute gets converted to a corresponding string
* name.
*/
/*implicit*/ MaterialAttributeData(MaterialAttribute name, MaterialAttributeType type, const void* value) noexcept;
/** @brief Attribute type */
MaterialAttributeType type() const { return _data.type; }
/**
* @brief Attribute name
*
* The returned view always has
* @ref Corrade::Containers::StringViewFlag::NullTerminated set.
*/
Containers::StringView name() const { return _data.data + 1; }
/**
* @brief Type-erased attribute value
*
* Cast the pointer to a concrete type based on @ref type().
*/
const void* value() const;
/**
* @brief Attribute value
*
* Expects that @p T corresponds to @ref type().
*/
template<class T> T value() const;
private:
friend MaterialData;
explicit MaterialAttributeData(Containers::StringView name, const MaterialAttributeType type, std::size_t typeSize, const void* value) noexcept;
/* Most of this is needed only for the constexpr constructor (yay C++),
the actual data layout is
|------------- x B ------------|
+------+------- .. -----+------+
| type | name .. \0 | data |
| 1 B | (x - n - 2) B | n B |
+------+------- .. -----+------+
where
- `x` is Implementation::MaterialAttributeDataSize,
- `type` is an 8-bit MaterialAttributeType,
- `data` is of size matching `type`, at the offset of
`(x - materialAttributeTypeSize(type))` B,
- `name` is a null-terminated string filling the rest.
This way the name is always at the same offset to make binary search
lookup fast and efficient, and data being at the end (instead of
right after the null-terminated string) makes them accessible in O(1)
as well. */
union ErasedScalar {
constexpr explicit ErasedScalar(Float value): f{value} {}
constexpr explicit ErasedScalar(Deg value): f{Float(value)} {}
constexpr explicit ErasedScalar(Rad value): f{Float(value)} {}
constexpr explicit ErasedScalar(UnsignedInt value): u{value} {}
constexpr explicit ErasedScalar(Int value): i{value} {}
Float f;
UnsignedInt u;
Int i;
};
template<std::size_t size> union ErasedVector {
constexpr explicit ErasedVector(const Math::Vector<size, Float>& value): f{value} {}
constexpr explicit ErasedVector(const Math::Vector<size, UnsignedInt>& value): u{value} {}
constexpr explicit ErasedVector(const Math::Vector<size, Int>& value): i{value} {}
Math::Vector<size, Float> f;
Math::Vector<size, UnsignedInt> u;
Math::Vector<size, Int> i;
};
template<std::size_t cols, std::size_t rows> union ErasedMatrix {
constexpr explicit ErasedMatrix(const Math::RectangularMatrix<cols, rows, Float>& value): a{value} {}
constexpr explicit ErasedMatrix(const Math::RectangularMatrix<rows, cols, Float>& value): b{value} {}
Math::RectangularMatrix<cols, rows, Float> a;
Math::RectangularMatrix<rows, cols, Float> b;
};
template<class T> struct Data {
template<class U, std::size_t ...sequence> constexpr explicit Data(MaterialAttributeType type, Containers::StringView name, const U& value, Math::Implementation::Sequence<sequence...>): type{type}, name{(sequence < name.size() ? name[sequence] : '\0')...}, value{value} {}
template<class U> constexpr explicit Data(MaterialAttributeType type, Containers::StringView name, const U& value): Data{type, name, value, typename Math::Implementation::GenerateSequence<63 - sizeof(T)>::Type{}} {}
MaterialAttributeType type;
char name[Implementation::MaterialAttributeDataSize - sizeof(MaterialAttributeType) - sizeof(T)];
T value;
};
union CORRADE_ALIGNAS(8) Storage {
constexpr explicit Storage() noexcept: data{} {}
template<class T> constexpr explicit Storage(typename std::enable_if<sizeof(T) == 1, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _1{type, name, value} {}
template<class T> constexpr explicit Storage(typename std::enable_if<sizeof(T) == 4, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _4{type, name, value} {}
template<class T> constexpr explicit Storage(typename std::enable_if<sizeof(T) == 8, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _8{type, name, value} {}
template<class T> constexpr explicit Storage(typename std::enable_if<sizeof(T) == 12, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _12{type, name, value} {}
template<class T> constexpr explicit Storage(typename std::enable_if<sizeof(T) == 16 && Math::IsVector<T>::value, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _16{type, name, value} {}
template<class T> constexpr explicit Storage(typename std::enable_if<sizeof(T) == 16 && !Math::IsVector<T>::value, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _16m{type, name, value} {}
template<class T> constexpr explicit Storage(typename std::enable_if<sizeof(T) == 24, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _24{type, name, value} {}
template<class T> constexpr explicit Storage(typename std::enable_if<sizeof(T) == 32, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _32{type, name, value} {}
template<class T> constexpr explicit Storage(typename std::enable_if<sizeof(T) == 36, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _36{type, name, value} {}
template<class T> constexpr explicit Storage(typename std::enable_if<sizeof(T) == 48, MaterialAttributeType>::type type, Containers::StringView name, const T& value) noexcept: _48{type, name, value} {}
MaterialAttributeType type;
char data[Implementation::MaterialAttributeDataSize];
Data<bool> _1;
Data<ErasedScalar> _4;
Data<ErasedVector<2>> _8;
Data<ErasedVector<3>> _12;
Data<ErasedVector<4>> _16;
Data<Math::RectangularMatrix<2, 2, Float>> _16m;
Data<ErasedMatrix<2, 3>> _24;
Data<ErasedMatrix<2, 4>> _32;
Data<Math::RectangularMatrix<3, 3, Float>> _36;
Data<ErasedMatrix<3, 4>> _48;
} _data;
static_assert(sizeof(Storage) == Implementation::MaterialAttributeDataSize, "something is off, huh");
};
/**
@brief Material data
@m_since_latest
Key-value store for material attributes in one of the types defined by
@ref MaterialAttributeType.
@section Trade-MaterialData-representation Internal representation
The attributes are stored sorted by the key in a contiguous array, with each
@ref MaterialAttributeData item occupying 64 bytes. The item contains a 1-byte
type identifier, the actual value and the rest is occupied with null-terminated
name. This means the name length can vary from 14 bytes for
@ref Magnum::Matrix3x4 "Matrix3x4" / @ref Magnum::Matrix4x3 "Matrix4x3" to 61
bytes for @cpp bool @ce (excluding null terminator). As each item has a fixed
size anyway, there's no value in supporting space-efficient 8-, 16- or
half-float types. Conversely, @ref Magnum::Double "Double" types are currently
not supported either as there isn't currently seen any need for extended
precision.
@m_class{m-block m-warning}
@par Max representable data size
With the current design, @ref MaterialAttributeData is 64 bytes and in
order to fit a type identifier and a string attribute name of a reasonable
length, the maximum data size is capped to 48 bytes. This means
@ref Magnum::Matrix4x4 "Matrix4x4" isn't listed among supported types, but
it shouldn't be a problem in practice --- ever an arbitrary color
correction matrix is just 3x4 values with the bottom row being always
@f$ \begin{pmatrix} 0 & 0 & 0 & 1 \end{pmatrix} @f$. This restriction might
get lifted eventually.
*/
class MAGNUM_TRADE_EXPORT MaterialData {
public:
/**
* @brief Construct
* @param data Attribute data
* @param importerState Importer-specific state
*
* The @p data gets sorted by name internally, expecting no duplicates.
*/
explicit MaterialData(Containers::Array<MaterialAttributeData>&& data, const void* importerState = nullptr) noexcept;
/** @overload */
/* Not noexcept because allocation happens inside */
explicit MaterialData(std::initializer_list<MaterialAttributeData> data, const void* importerState = nullptr);
/**
* @brief Construct a non-owned material data
* @param dataFlags Ignored. Used only for a safer distinction
* from the owning constructor.
* @param data Attribute data
* @param importerState Importer-specific state
*
* The @p data is expected to be already sorted by name, without
* duplicates.
*/
explicit MaterialData(DataFlags dataFlags, Containers::ArrayView<const MaterialAttributeData> data, const void* importerState = nullptr) noexcept;
~MaterialData();
/** @brief Copying is not allowed */
MaterialData(const MaterialData&) = delete;
/** @brief Move constructor */
MaterialData(MaterialData&&) noexcept;
/** @brief Copying is not allowed */
MaterialData& operator=(const MaterialData&) = delete;
/** @brief Move assignment */
MaterialData& operator=(MaterialData&&) noexcept;
/**
* @brief Raw attribute data
*
* Returns @cpp nullptr @ce if the material has no attributes.
* @see @ref release()
*/
Containers::ArrayView<const MaterialAttributeData> data() const { return _data; }
/** @brief Attribute count */
UnsignedInt attributeCount() const { return _data.size(); }
/** @brief Whether the material has given attribute */
bool hasAttribute(Containers::StringView name) const;
bool hasAttribute(MaterialAttribute name) const; /**< @overload */
/**
* @brief ID of a named attribute
*
* The @p name is expected to exist.
*/
UnsignedInt attributeId(Containers::StringView name) const;
UnsignedInt attributeId(MaterialAttribute name) const; /**< @overload */
/**
* @brief Attribute name
*
* The @p id is expected to be smaller than @ref attributeCount() const.
* The returned view always has
* @ref Corrade::Containers::StringViewFlag::NullTerminated set.
* @see @ref attributeType()
*/
Containers::StringView attributeName(UnsignedInt id) const;
/**
* @brief Attribute type
*
* The @p id is expected to be smaller than @ref attributeCount() const.
* @see @ref attributeName()
*/
MaterialAttributeType attributeType(UnsignedInt id) const;
/**
* @brief Type of a named attribute
*
* The @p name is expected to exist.
* @see @ref hasAttribute()
*/
MaterialAttributeType attributeType(Containers::StringView name) const;
/** @overload */
MaterialAttributeType attributeType(MaterialAttribute name) const;
/**
* @brief Type-erased attribute value
*
* The @p id is expected to be smaller than @ref attributeCount() const.
* Cast the pointer to a concrete type based on @ref type().
*/
const void* attribute(UnsignedInt id) const;
/**
* @brief Type-erased value of a named attribute
*
* The @p name is expected to exist. Cast the pointer to a concrete
* type based on @ref attributeType().
* @see @ref hasAttribute()
*/
const void* attribute(Containers::StringView name) const;
const void* attribute(MaterialAttribute name) const; /**< @overload */
/**
* @brief Attribute value
*
* The @p id is expected to be smaller than @ref attributeCount() const.
* Expects that @p T corresponds to @ref attributeType(UnsignedInt) const
* for given @p id.
*/
template<class T> T attribute(UnsignedInt id) const;
/**
* @brief Value of a named attribute
*
* The @p name is expected to exist. Expects that @p T corresponds to
* @ref attributeType(Containers::StringView) const for given @p name.
* @see @ref hasAttribute()
*/
template<class T> T attribute(Containers::StringView name) const;
template<class T> T attribute(MaterialAttribute name) const; /**< @overload */
/**
* @brief Release data storage
*
* Releases the ownership of the attribute array and resets internal
* state to default. The material then behaves like if it has no
* attributes. Note that the returned array has a custom no-op
* deleter when the data are not owned by the mesh, and while the
* returned array type is mutable, the actual memory might be not.
* @see @ref data()
*/
Containers::Array<MaterialAttributeData> release();
/**
* @brief Importer-specific state
*
* See @ref AbstractImporter::importerState() for more information.
*/
const void* importerState() const { return _importerState; }
private:
static Containers::StringView attributeString(MaterialAttribute name);
/* Internal helper that doesn't assert, unlike attributeId() */
UnsignedInt attributeFor(Containers::StringView name) const;
Containers::Array<MaterialAttributeData> _data;
const void* _importerState;
};
namespace Implementation {
/* LCOV_EXCL_START */
template<class T> struct MaterialAttributeTypeFor {
/* C++ why there isn't an obvious way to do such a thing?! */
static_assert(sizeof(T) == 0, "unsupported attribute type");
};
template<> struct MaterialAttributeTypeFor<bool> {
constexpr static MaterialAttributeType type() {
return MaterialAttributeType::Bool;
}
};
#ifndef DOXYGEN_GENERATING_OUTPUT
#define _c(type_) template<> struct MaterialAttributeTypeFor<type_> { \
constexpr static MaterialAttributeType type() { \
return MaterialAttributeType::type_; \
} \
};
_c(Float)
_c(Deg)
_c(Rad)
_c(UnsignedInt)
_c(Int)
_c(Vector2)
_c(Vector2ui)
_c(Vector2i)
_c(Vector3)
_c(Vector3ui)
_c(Vector3i)
_c(Vector4)
_c(Vector4ui)
_c(Vector4i)
_c(Matrix2x2)
_c(Matrix2x3)
_c(Matrix2x4)
_c(Matrix3x2)
_c(Matrix3x3)
_c(Matrix3x4)
_c(Matrix4x2)
_c(Matrix4x3)
#undef _c
#endif
template<> struct MaterialAttributeTypeFor<Color3>: MaterialAttributeTypeFor<Vector3> {};
template<> struct MaterialAttributeTypeFor<Color4>: MaterialAttributeTypeFor<Vector4> {};
template<> struct MaterialAttributeTypeFor<Matrix3>: MaterialAttributeTypeFor<Matrix3x3> {};
/* LCOV_EXCL_STOP */
}
/* The 2 extra bytes are for a null byte after the name and a type */
template<class T> constexpr MaterialAttributeData::MaterialAttributeData(const Containers::StringView name, const T& value) noexcept: _data{Implementation::MaterialAttributeTypeFor<T>::type(), (CORRADE_CONSTEXPR_ASSERT(name.size() + sizeof(T) + 2 <= Implementation::MaterialAttributeDataSize, "Trade::MaterialAttributeData: name" << name << "too long, expected at most" << Implementation::MaterialAttributeDataSize - sizeof(T) - 2 << "bytes for" << Implementation::MaterialAttributeTypeFor<T>::type() << "but got" << name.size()), name), value} {}
template<class T> T MaterialAttributeData::value() const {
CORRADE_ASSERT(Implementation::MaterialAttributeTypeFor<T>::type() == _data.type,
"Trade::MaterialAttributeData::value(): improper type requested for" << (_data.data + 1) << "of" << _data.type, {});
return *reinterpret_cast<const T*>(value());
}
template<class T> T MaterialData::attribute(UnsignedInt id) const {
const void* const value = attribute(id);
#ifdef CORRADE_GRACEFUL_ASSERT
if(!value) return {};
#endif
CORRADE_ASSERT(Implementation::MaterialAttributeTypeFor<T>::type() == _data[id]._data.type,
"Trade::MaterialData::attribute(): improper type requested for" << (_data[id]._data.data + 1) << "of" << _data[id]._data.type, {});
return *reinterpret_cast<const T*>(value);
}
template<class T> T MaterialData::attribute(Containers::StringView name) const {
const UnsignedInt id = attributeFor(name);
CORRADE_ASSERT(id != ~UnsignedInt{},
"Trade::MaterialData::attribute(): attribute" << name << "not found", {});
return attribute<T>(id);
}
template<class T> T MaterialData::attribute(MaterialAttribute name) const {
const Containers::StringView string = attributeString(name);
CORRADE_ASSERT(string.data(), "Trade::MaterialData::attribute(): invalid name" << name, {});
return attribute<T>(string);
}
}}
#endif

1
src/Magnum/Trade/Test/CMakeLists.txt

@ -60,6 +60,7 @@ corrade_add_test(TradeTextureDataTest TextureDataTest.cpp LIBRARIES MagnumTrade)
set_property(TARGET
TradeAnimationDataTest
TradeMaterialDataTest
TradeMeshDataTest
APPEND PROPERTY COMPILE_DEFINITIONS "CORRADE_GRACEFUL_ASSERT")

732
src/Magnum/Trade/Test/MaterialDataTest.cpp

@ -23,10 +23,16 @@
DEALINGS IN THE SOFTWARE.
*/
#include <algorithm>
#include <sstream>
#include <Corrade/Containers/StaticArray.h>
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/TestSuite/Compare/Numeric.h>
#include <Corrade/Utility/DebugStl.h>
#include <Corrade/Utility/String.h>
#include "Magnum/Math/Matrix3.h"
#include "Magnum/Trade/MaterialData.h"
#include "Magnum/Trade/PhongMaterialData.h"
namespace Magnum { namespace Trade { namespace Test { namespace {
@ -35,6 +41,45 @@ class MaterialDataTest: public TestSuite::Tester {
public:
explicit MaterialDataTest();
void attributeTypeSize();
void attributeTypeSizeInvalid();
void attributeMap();
void constructAttributeDefault();
void constructAttributeString();
void constructAttributeName();
void constructAttributeTypeErasedString();
void constructAttributeTypeErasedName();
template<class T> void constructAttributeStringConstexpr();
void constructAttributeInvalidName();
void constructAttributeWrongTypeForName();
void constructAttributeInvalidType();
void constructAttributeTooLarge();
void constructAttributeWrongAccessType();
void construct();
void constructEmptyAttribute();
void constructDuplicateAttribute();
void constructFromImmutableSortedArray();
void constructNonOwned();
void constructNonOwnedEmptyAttribute();
void constructNonOwnedNotSorted();
void constructNonOwnedDuplicateAttribute();
void constructCopy();
void constructMove();
void accessOutOfBounds();
void accessInvalidAttributeName();
void accessNotFound();
void accessWrongType();
void release();
void constructPhong();
void constructPhongTextured();
void constructPhongTexturedTextureTransform();
@ -42,11 +87,14 @@ class MaterialDataTest: public TestSuite::Tester {
void constructPhongTextureTransformNoTextures();
void constructPhongNoTextureTransformationFlag();
void constructPhongNoTextureCoordinatesFlag();
void constructCopy();
void constructMovePhong();
void constructPhongCopy();
void constructPhongMove();
void accessInvalidTextures();
void debugAttribute();
void debugAttributeType();
void debugType();
void debugFlag();
void debugFlags();
@ -57,18 +105,84 @@ class MaterialDataTest: public TestSuite::Tester {
};
MaterialDataTest::MaterialDataTest() {
addTests({&MaterialDataTest::constructPhong,
addTests({&MaterialDataTest::attributeTypeSize,
&MaterialDataTest::attributeTypeSizeInvalid,
&MaterialDataTest::attributeMap,
&MaterialDataTest::constructAttributeDefault,
&MaterialDataTest::constructAttributeString,
&MaterialDataTest::constructAttributeName,
&MaterialDataTest::constructAttributeTypeErasedString,
&MaterialDataTest::constructAttributeTypeErasedName,
&MaterialDataTest::constructAttributeStringConstexpr<bool>,
&MaterialDataTest::constructAttributeStringConstexpr<Float>,
&MaterialDataTest::constructAttributeStringConstexpr<Deg>,
&MaterialDataTest::constructAttributeStringConstexpr<Rad>,
&MaterialDataTest::constructAttributeStringConstexpr<UnsignedInt>,
&MaterialDataTest::constructAttributeStringConstexpr<Int>,
&MaterialDataTest::constructAttributeStringConstexpr<Vector2>,
&MaterialDataTest::constructAttributeStringConstexpr<Vector2ui>,
&MaterialDataTest::constructAttributeStringConstexpr<Vector2i>,
&MaterialDataTest::constructAttributeStringConstexpr<Vector3>,
&MaterialDataTest::constructAttributeStringConstexpr<Vector3ui>,
&MaterialDataTest::constructAttributeStringConstexpr<Vector3i>,
&MaterialDataTest::constructAttributeStringConstexpr<Vector4>,
&MaterialDataTest::constructAttributeStringConstexpr<Vector4ui>,
&MaterialDataTest::constructAttributeStringConstexpr<Vector4i>,
&MaterialDataTest::constructAttributeStringConstexpr<Matrix2x2>,
&MaterialDataTest::constructAttributeStringConstexpr<Matrix2x3>,
&MaterialDataTest::constructAttributeStringConstexpr<Matrix2x4>,
&MaterialDataTest::constructAttributeStringConstexpr<Matrix3x2>,
&MaterialDataTest::constructAttributeStringConstexpr<Matrix3x3>,
&MaterialDataTest::constructAttributeStringConstexpr<Matrix3x4>,
&MaterialDataTest::constructAttributeStringConstexpr<Matrix4x2>,
&MaterialDataTest::constructAttributeStringConstexpr<Matrix4x3>,
&MaterialDataTest::constructAttributeInvalidName,
&MaterialDataTest::constructAttributeWrongTypeForName,
&MaterialDataTest::constructAttributeInvalidType,
&MaterialDataTest::constructAttributeTooLarge,
&MaterialDataTest::constructAttributeWrongAccessType,
&MaterialDataTest::construct,
&MaterialDataTest::constructEmptyAttribute});
addRepeatedTests({&MaterialDataTest::constructDuplicateAttribute},
5*4*3*2);
addTests({&MaterialDataTest::constructFromImmutableSortedArray,
&MaterialDataTest::constructNonOwned,
&MaterialDataTest::constructNonOwnedEmptyAttribute,
&MaterialDataTest::constructNonOwnedNotSorted,
&MaterialDataTest::constructNonOwnedDuplicateAttribute,
&MaterialDataTest::constructCopy,
&MaterialDataTest::constructMove,
&MaterialDataTest::accessOutOfBounds,
&MaterialDataTest::accessInvalidAttributeName,
&MaterialDataTest::accessNotFound,
&MaterialDataTest::accessWrongType,
&MaterialDataTest::release,
&MaterialDataTest::constructPhong,
&MaterialDataTest::constructPhongTextured,
&MaterialDataTest::constructPhongTexturedTextureTransform,
&MaterialDataTest::constructPhongTexturedCoordinates,
&MaterialDataTest::constructPhongTextureTransformNoTextures,
&MaterialDataTest::constructPhongNoTextureTransformationFlag,
&MaterialDataTest::constructPhongNoTextureCoordinatesFlag,
&MaterialDataTest::constructCopy,
&MaterialDataTest::constructMovePhong,
&MaterialDataTest::constructPhongCopy,
&MaterialDataTest::constructPhongMove,
&MaterialDataTest::accessInvalidTextures,
&MaterialDataTest::debugAttribute,
&MaterialDataTest::debugAttributeType,
&MaterialDataTest::debugType,
&MaterialDataTest::debugFlag,
&MaterialDataTest::debugFlags,
@ -78,6 +192,594 @@ MaterialDataTest::MaterialDataTest() {
&MaterialDataTest::debugPhongFlags});
}
using namespace Containers::Literals;
using namespace Math::Literals;
void MaterialDataTest::attributeTypeSize() {
CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::Bool), 1);
CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::Deg), 4);
CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::Vector2i), 8);
CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::Vector3), 12);
CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::Vector4ui), 16);
CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::Matrix2x3), 24);
CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::Matrix4x2), 32);
CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::Matrix3x3), 36);
CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::Matrix3x4), 48);
}
void MaterialDataTest::attributeTypeSizeInvalid() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
std::ostringstream out;
Error redirectError{&out};
materialAttributeTypeSize(MaterialAttributeType(0x0));
materialAttributeTypeSize(MaterialAttributeType(0xfe));
CORRADE_COMPARE(out.str(),
"Trade::materialAttributeTypeSize(): invalid type Trade::MaterialAttributeType(0x0)\n"
"Trade::materialAttributeTypeSize(): invalid type Trade::MaterialAttributeType(0xfe)\n");
}
void MaterialDataTest::attributeMap() {
/* Ensure all attribute names are:
- present in the map,
- that their translated string name corresponds to the enum value name,
- that the calculated type size corresponds to the actual type,
- and that the name together with the type fits.
This goes through the first 16 bits, which should be enough. Going
through 32 bits takes 8 seconds, too much. */
for(UnsignedInt i = 1; i <= 0xffff; ++i) {
/* Attribute 0 reserved for an invalid value */
const auto attribute = MaterialAttribute(i);
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic error "-Wswitch"
#endif
switch(attribute) {
#define _c(name_, type) \
case MaterialAttribute::name_: \
CORRADE_COMPARE((MaterialAttributeData{MaterialAttribute::name_, type{}}.name()), #name_); \
CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::type), sizeof(type)); \
CORRADE_COMPARE_AS(sizeof(type) + Containers::arraySize(#name_) + sizeof(MaterialAttributeType), sizeof(MaterialAttributeData), TestSuite::Compare::LessOrEqual); \
break;
#define _ct(name_, typeName, type) \
case MaterialAttribute::name_: \
CORRADE_COMPARE((MaterialAttributeData{MaterialAttribute::name_, type{}}.name()), #name_); \
CORRADE_COMPARE(materialAttributeTypeSize(MaterialAttributeType::typeName), sizeof(type)); \
CORRADE_COMPARE_AS(sizeof(type) + Containers::arraySize(#name_) + sizeof(MaterialAttributeType), sizeof(MaterialAttributeData), TestSuite::Compare::LessOrEqual); \
break;
#include "Magnum/Trade/Implementation/materialAttributeProperties.hpp"
#undef _c
#undef _ct
}
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
}
}
void MaterialDataTest::constructAttributeDefault() {
MaterialAttributeData attribute;
CORRADE_COMPARE(attribute.name(), "");
CORRADE_COMPARE(attribute.type(), MaterialAttributeType{});
constexpr MaterialAttributeData cattribute;
CORRADE_COMPARE(cattribute.name(), "");
CORRADE_COMPARE(cattribute.type(), MaterialAttributeType{});
}
void MaterialDataTest::constructAttributeString() {
MaterialAttributeData attribute{"colorTransform", Matrix3::scaling({2.0f, 0.3f})};
CORRADE_COMPARE(attribute.name(), "colorTransform");
CORRADE_COMPARE(attribute.name().flags(), Containers::StringViewFlag::NullTerminated);
CORRADE_COMPARE(attribute.name()[attribute.name().size()], '\0');
CORRADE_COMPARE(attribute.type(), MaterialAttributeType::Matrix3x3);
CORRADE_COMPARE(attribute.value<Matrix3>(), Matrix3::scaling({2.0f, 0.3f}));
CORRADE_COMPARE(*reinterpret_cast<const Matrix3*>(attribute.value()), Matrix3::scaling({2.0f, 0.3f}));
}
void MaterialDataTest::constructAttributeName() {
MaterialAttributeData attribute{MaterialAttribute::DiffuseColor, 0xff3366aa_rgbaf};
CORRADE_COMPARE(attribute.name(), "DiffuseColor"_s);
CORRADE_COMPARE(attribute.name().flags(), Containers::StringViewFlag::NullTerminated);
CORRADE_COMPARE(attribute.name()[attribute.name().size()], '\0');
CORRADE_COMPARE(attribute.type(), MaterialAttributeType::Vector4);
CORRADE_COMPARE(attribute.value<Color4>(), 0xff3366aa_rgbaf);
CORRADE_COMPARE(*reinterpret_cast<const Color4*>(attribute.value()), 0xff3366aa_rgbaf);
}
void MaterialDataTest::constructAttributeTypeErasedString() {
const Vector2i data{37, -458};
MaterialAttributeData attribute{"millibitsOfInformation", MaterialAttributeType::Vector2i, &data};
CORRADE_COMPARE(attribute.name(), "millibitsOfInformation");
CORRADE_COMPARE(attribute.name().flags(), Containers::StringViewFlag::NullTerminated);
CORRADE_COMPARE(attribute.name()[attribute.name().size()], '\0');
CORRADE_COMPARE(attribute.type(), MaterialAttributeType::Vector2i);
CORRADE_COMPARE(attribute.value<Vector2i>(), (Vector2i{37, -458}));
}
void MaterialDataTest::constructAttributeTypeErasedName() {
const Float data = 85.1f;
MaterialAttributeData attribute{MaterialAttribute::Shininess, MaterialAttributeType::Float, &data};
CORRADE_COMPARE(attribute.name(), "Shininess");
CORRADE_COMPARE(attribute.name().flags(), Containers::StringViewFlag::NullTerminated);
CORRADE_COMPARE(attribute.name()[attribute.name().size()], '\0');
CORRADE_COMPARE(attribute.type(), MaterialAttributeType::Float);
CORRADE_COMPARE(attribute.value<Float>(), 85.1f);
}
template<class T> struct TypeName {
static const char* name() { return Math::TypeTraits<T>::name(); }
};
template<> struct TypeName<bool> {
static const char* name() { return "bool"; }
};
#define _c(type) template<> struct TypeName<type> { \
static const char* name() { return #type; } \
};
_c(Deg)
_c(Rad)
_c(Vector2)
_c(Vector2i)
_c(Vector2ui)
_c(Vector3)
_c(Vector3i)
_c(Vector3ui)
_c(Vector4)
_c(Vector4i)
_c(Vector4ui)
_c(Matrix2x2)
_c(Matrix2x3)
_c(Matrix2x4)
_c(Matrix3x2)
_c(Matrix3x3)
_c(Matrix3x4)
_c(Matrix4x2)
_c(Matrix4x3)
#undef _c
template<class T> void MaterialDataTest::constructAttributeStringConstexpr() {
setTestCaseTemplateName(TypeName<T>::name());
/* "templateAttrib" is 14 chars, which is the maximum for 48-bit types */
constexpr MaterialAttributeData attribute{"templateAttrib"_s, T(15)};
CORRADE_COMPARE(attribute.name(), "templateAttrib");
CORRADE_COMPARE(attribute.name().flags(), Containers::StringViewFlag::NullTerminated);
CORRADE_COMPARE(attribute.name()[attribute.name().size()], '\0');
CORRADE_COMPARE(attribute.value<T>(), T(15));
}
void MaterialDataTest::constructAttributeInvalidName() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
std::ostringstream out;
Error redirectError{&out};
MaterialAttributeData{MaterialAttribute(0x0), 5};
MaterialAttributeData{MaterialAttribute(0xfefe), 5};
CORRADE_COMPARE(out.str(),
"Trade::MaterialAttributeData: invalid name Trade::MaterialAttribute(0x0)\n"
"Trade::MaterialAttributeData: invalid name Trade::MaterialAttribute(0xfefe)\n");
}
void MaterialDataTest::constructAttributeWrongTypeForName() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
std::ostringstream out;
Error redirectError{&out};
MaterialAttributeData{MaterialAttribute::DiffuseColor, Vector3ui{255, 16, 24}};
CORRADE_COMPARE(out.str(),
"Trade::MaterialAttributeData: expected Trade::MaterialAttributeType::Vector4 for Trade::MaterialAttribute::DiffuseColor but got Trade::MaterialAttributeType::Vector3ui\n");
}
void MaterialDataTest::constructAttributeInvalidType() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
std::ostringstream out;
Error redirectError{&out};
MaterialAttributeData{"bla", MaterialAttributeType(0x0), nullptr};
MaterialAttributeData{"bla", MaterialAttributeType(0xfe), nullptr};
CORRADE_COMPARE(out.str(),
"Trade::materialAttributeTypeSize(): invalid type Trade::MaterialAttributeType(0x0)\n"
"Trade::materialAttributeTypeSize(): invalid type Trade::MaterialAttributeType(0xfe)\n");
}
void MaterialDataTest::constructAttributeTooLarge() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
std::ostringstream out;
Error redirectError{&out};
MaterialAttributeData{"attributeIsLong", Matrix3x4{}};
/* Constexpr variant has the same assert, but in the header. It should have
the same output. */
/*constexpr*/ MaterialAttributeData{"attributeIsLong"_s, Matrix3x4{}};
CORRADE_COMPARE(out.str(),
"Trade::MaterialAttributeData: name attributeIsLong too long, expected at most 14 bytes for Trade::MaterialAttributeType::Matrix3x4 but got 15\n"
"Trade::MaterialAttributeData: name attributeIsLong too long, expected at most 14 bytes for Trade::MaterialAttributeType::Matrix3x4 but got 15\n");
}
void MaterialDataTest::constructAttributeWrongAccessType() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
std::ostringstream out;
Error redirectError{&out};
MaterialAttributeData{"thing3", Matrix4x3{}}.value<Int>();
CORRADE_COMPARE(out.str(), "Trade::MaterialAttributeData::value(): improper type requested for thing3 of Trade::MaterialAttributeType::Matrix4x3\n");
}
void MaterialDataTest::construct() {
int state;
MaterialData data{{
{MaterialAttribute::DoubleSided, true},
{MaterialAttribute::DiffuseTextureCoordinates, 5u},
{"highlightColor", 0x335566ff_rgbaf},
{MaterialAttribute::AmbientTextureMatrix, Matrix3::scaling({0.5f, 1.0f})}
}, &state};
CORRADE_COMPARE(data.attributeCount(), 4);
CORRADE_COMPARE(data.data().size(), 4);
CORRADE_COMPARE(data.importerState(), &state);
/* Verify sorting */
CORRADE_COMPARE(data.attributeName(0), "AmbientTextureMatrix");
CORRADE_COMPARE(data.attributeName(1), "DiffuseTextureCoordinates");
CORRADE_COMPARE(data.attributeName(2), "DoubleSided");
CORRADE_COMPARE(data.attributeName(3), "highlightColor");
/* Access by ID */
CORRADE_COMPARE(data.attributeType(0), MaterialAttributeType::Matrix3x3);
CORRADE_COMPARE(data.attributeType(1), MaterialAttributeType::UnsignedInt);
CORRADE_COMPARE(data.attributeType(2), MaterialAttributeType::Bool);
CORRADE_COMPARE(data.attributeType(3), MaterialAttributeType::Vector4);
CORRADE_COMPARE(data.attribute<Matrix3>(0), Matrix3::scaling({0.5f, 1.0f}));
CORRADE_COMPARE(data.attribute<UnsignedInt>(1), 5);
CORRADE_COMPARE(data.attribute<bool>(2), true);
CORRADE_COMPARE(data.attribute<Color4>(3), 0x335566ff_rgbaf);
CORRADE_COMPARE(*static_cast<const Matrix3*>(data.attribute(0)), Matrix3::scaling({0.5f, 1.0f}));
CORRADE_COMPARE(*static_cast<const UnsignedInt*>(data.attribute(1)), 5);
CORRADE_COMPARE(*static_cast<const bool*>(data.attribute(2)), true);
CORRADE_COMPARE(*static_cast<const Color4*>(data.attribute(3)), 0x335566ff_rgbaf);
/* Access by name */
CORRADE_VERIFY(data.hasAttribute(MaterialAttribute::DoubleSided));
CORRADE_VERIFY(data.hasAttribute(MaterialAttribute::AmbientTextureMatrix));
CORRADE_VERIFY(!data.hasAttribute(MaterialAttribute::TextureMatrix));
CORRADE_COMPARE(data.attributeId(MaterialAttribute::DoubleSided), 2);
CORRADE_COMPARE(data.attributeId(MaterialAttribute::AmbientTextureMatrix), 0);
CORRADE_COMPARE(data.attributeId(MaterialAttribute::DiffuseTextureCoordinates), 1);
CORRADE_COMPARE(data.attributeType(MaterialAttribute::AmbientTextureMatrix), MaterialAttributeType::Matrix3x3);
CORRADE_COMPARE(data.attributeType(MaterialAttribute::DiffuseTextureCoordinates), MaterialAttributeType::UnsignedInt);
CORRADE_COMPARE(data.attributeType(MaterialAttribute::DoubleSided), MaterialAttributeType::Bool);
CORRADE_COMPARE(data.attribute<Matrix3>(MaterialAttribute::AmbientTextureMatrix), Matrix3::scaling({0.5f, 1.0f}));
CORRADE_COMPARE(data.attribute<UnsignedInt>(MaterialAttribute::DiffuseTextureCoordinates), 5);
CORRADE_COMPARE(data.attribute<bool>(MaterialAttribute::DoubleSided), true);
CORRADE_COMPARE(*static_cast<const Matrix3*>(data.attribute(MaterialAttribute::AmbientTextureMatrix)), Matrix3::scaling({0.5f, 1.0f}));
CORRADE_COMPARE(*static_cast<const UnsignedInt*>(data.attribute(MaterialAttribute::DiffuseTextureCoordinates)), 5);
CORRADE_COMPARE(*static_cast<const bool*>(data.attribute(MaterialAttribute::DoubleSided)), true);
/* Access by string */
CORRADE_VERIFY(data.hasAttribute("DoubleSided"));
CORRADE_VERIFY(data.hasAttribute("highlightColor"));
CORRADE_VERIFY(!data.hasAttribute("TextureMatrix"));
CORRADE_COMPARE(data.attributeId("DoubleSided"), 2);
CORRADE_COMPARE(data.attributeId("AmbientTextureMatrix"), 0);
CORRADE_COMPARE(data.attributeId("DiffuseTextureCoordinates"), 1);
CORRADE_COMPARE(data.attributeId("highlightColor"), 3);
CORRADE_COMPARE(data.attributeType("AmbientTextureMatrix"), MaterialAttributeType::Matrix3x3);
CORRADE_COMPARE(data.attributeType("DiffuseTextureCoordinates"), MaterialAttributeType::UnsignedInt);
CORRADE_COMPARE(data.attributeType("DoubleSided"), MaterialAttributeType::Bool);
CORRADE_COMPARE(data.attributeType("highlightColor"), MaterialAttributeType::Vector4);
CORRADE_COMPARE(data.attribute<Matrix3>("AmbientTextureMatrix"), Matrix3::scaling({0.5f, 1.0f}));
CORRADE_COMPARE(data.attribute<UnsignedInt>("DiffuseTextureCoordinates"), 5);
CORRADE_COMPARE(data.attribute<bool>("DoubleSided"), true);
CORRADE_COMPARE(data.attribute<Color4>("highlightColor"), 0x335566ff_rgbaf);
CORRADE_COMPARE(*static_cast<const Matrix3*>(data.attribute("AmbientTextureMatrix")), Matrix3::scaling({0.5f, 1.0f}));
CORRADE_COMPARE(*static_cast<const UnsignedInt*>(data.attribute("DiffuseTextureCoordinates")), 5);
CORRADE_COMPARE(*static_cast<const bool*>(data.attribute("DoubleSided")), true);
CORRADE_COMPARE(*static_cast<const Color4*>(data.attribute("highlightColor")), 0x335566ff_rgbaf);
}
void MaterialDataTest::constructEmptyAttribute() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
std::ostringstream out;
Error redirectError{&out};
MaterialData{{
{"DiffuseTexture"_s, 12u},
MaterialAttributeData{}
}};
CORRADE_COMPARE(out.str(), "Trade::MaterialData: attribute 1 doesn't specify anything\n");
}
void MaterialDataTest::constructDuplicateAttribute() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
auto attributes = Containers::Array<MaterialAttributeData>{Containers::InPlaceInit, {
{MaterialAttribute::DoubleSided, true},
{MaterialAttribute::DiffuseTextureCoordinates, 5u},
{"highlightColor", 0x335566ff_rgbaf},
{MaterialAttribute::AmbientTextureMatrix, Matrix3::scaling({0.5f, 1.0f})},
{MaterialAttribute::DiffuseTextureCoordinates, 5u}
}};
/* Testing that it asserts in all input permutations */
for(std::size_t i = 0; i != testCaseRepeatId(); ++i)
std::next_permutation(attributes.begin(), attributes.end(), [](const MaterialAttributeData& a, const MaterialAttributeData& b) {
return a.name() < b.name();
});
std::ostringstream out;
Error redirectError{&out};
MaterialData data{std::move(attributes)};
/* Because with graceful asserts it doesn't exit on error, the assertion
might get printed multiple times */
CORRADE_COMPARE(Utility::String::partition(out.str(), '\n')[0], "Trade::MaterialData: duplicate attribute DiffuseTextureCoordinates");
}
void MaterialDataTest::constructFromImmutableSortedArray() {
constexpr MaterialAttributeData attributes[]{
{"hello this is first"_s, 1},
{"yay this is last"_s, Vector4{0.2f, 0.6f, 0.4f, 1.0f}}
};
MaterialData data{Containers::Array<MaterialAttributeData>{const_cast<MaterialAttributeData*>(attributes), Containers::arraySize(attributes), [](MaterialAttributeData*, std::size_t) {}}};
CORRADE_COMPARE(data.attributeCount(), 2);
CORRADE_COMPARE(data.attributeName(0), "hello this is first");
CORRADE_COMPARE(data.attributeName(1), "yay this is last");
}
void MaterialDataTest::constructNonOwned() {
constexpr MaterialAttributeData attributes[]{
{"AmbientTextureMatrix"_s, Matrix3{{0.5f, 0.0f, 0.0f},
{0.0f, 1.0f, 0.0f},
{0.0f, 0.0f, 1.0f}}},
{"DiffuseTextureCoordinates"_s, 5u},
{"DoubleSided"_s, true},
{"highlightColor"_s, Vector4{0.2f, 0.6f, 0.4f, 1.0f}}
};
int state;
MaterialData data{{}, attributes, &state};
/* Expecting the same output as in construct() */
CORRADE_COMPARE(data.attributeCount(), 4);
CORRADE_COMPARE(data.data().size(), 4);
CORRADE_COMPARE(data.data().data(), attributes);
CORRADE_COMPARE(data.importerState(), &state);
/* We sorted the input already */
CORRADE_COMPARE(data.attributeName(0), "AmbientTextureMatrix");
CORRADE_COMPARE(data.attributeName(1), "DiffuseTextureCoordinates");
CORRADE_COMPARE(data.attributeName(2), "DoubleSided");
CORRADE_COMPARE(data.attributeName(3), "highlightColor");
/* No need to verify the contents as there's no difference in access in
owned vs non-owned */
}
void MaterialDataTest::constructNonOwnedEmptyAttribute() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
MaterialAttributeData attributes[]{
{"DiffuseTexture"_s, 12u},
MaterialAttributeData{}
};
std::ostringstream out;
Error redirectError{&out};
/* nullptr to avoid attributes interpreted as importerState */
MaterialData{{}, attributes, nullptr};
CORRADE_COMPARE(out.str(), "Trade::MaterialData: attribute 1 doesn't specify anything\n");
}
void MaterialDataTest::constructNonOwnedNotSorted() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
MaterialAttributeData attributes[]{
{"DiffuseTextureCoordinates"_s, 5u},
{"DiffuseTexture"_s, 12u}
};
std::ostringstream out;
Error redirectError{&out};
/* nullptr to avoid attributes interpreted as importerState */
MaterialData{{}, attributes, nullptr};
CORRADE_COMPARE(out.str(), "Trade::MaterialData: DiffuseTexture has to be sorted before DiffuseTextureCoordinates if passing non-owned data\n");
}
void MaterialDataTest::constructNonOwnedDuplicateAttribute() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
MaterialAttributeData attributes[]{
{"DiffuseTexture"_s, 35u},
{"DiffuseTextureCoordinates"_s, 5u},
{"DiffuseTextureCoordinates"_s, 12u}
};
std::ostringstream out;
Error redirectError{&out};
/* nullptr to avoid attributes interpreted as importerState */
MaterialData{{}, attributes, nullptr};
CORRADE_COMPARE(out.str(), "Trade::MaterialData: duplicate attribute DiffuseTextureCoordinates\n");
}
void MaterialDataTest::constructCopy() {
CORRADE_VERIFY(!(std::is_constructible<MaterialData, const MaterialData&>{}));
CORRADE_VERIFY(!(std::is_assignable<MaterialData, const MaterialData&>{}));
}
void MaterialDataTest::constructMove() {
int state;
MaterialData a{{
{MaterialAttribute::DoubleSided, true},
{"boredomFactor", 5}
}, &state};
MaterialData b{std::move(a)};
CORRADE_COMPARE(a.attributeCount(), 0);
CORRADE_COMPARE(b.attributeCount(), 2);
CORRADE_COMPARE(b.attributeName(0), "DoubleSided");
CORRADE_COMPARE(b.importerState(), &state);
MaterialData c{{
{MaterialAttribute::AlphaMask, 0.5f}
}};
c = std::move(b);
CORRADE_COMPARE(b.attributeCount(), 1);
CORRADE_COMPARE(c.attributeCount(), 2);
CORRADE_COMPARE(c.attributeName(0), "DoubleSided");
CORRADE_COMPARE(c.importerState(), &state);
CORRADE_VERIFY(std::is_nothrow_move_constructible<MaterialData>::value);
CORRADE_VERIFY(std::is_nothrow_move_assignable<MaterialData>::value);
}
void MaterialDataTest::accessOutOfBounds() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
MaterialData data{{
{MaterialAttribute::AlphaMask, 0.5f},
{MaterialAttribute::SpecularTexture, 3u}
}};
std::ostringstream out;
Error redirectError{&out};
data.attributeName(2);
data.attributeType(2);
data.attribute(2);
data.attribute<Int>(2);
CORRADE_COMPARE(out.str(),
"Trade::MaterialData::attributeName(): index 2 out of range for 2 attributes\n"
"Trade::MaterialData::attributeType(): index 2 out of range for 2 attributes\n"
"Trade::MaterialData::attribute(): index 2 out of range for 2 attributes\n"
"Trade::MaterialData::attribute(): index 2 out of range for 2 attributes\n");
}
void MaterialDataTest::accessInvalidAttributeName() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
MaterialData data{};
std::ostringstream out;
Error redirectError{&out};
data.hasAttribute(MaterialAttribute(0x0));
data.hasAttribute(MaterialAttribute(0xfefe));
data.attributeId(MaterialAttribute(0x0));
data.attributeId(MaterialAttribute(0xfefe));
data.attributeType(MaterialAttribute(0x0));
data.attributeType(MaterialAttribute(0xfefe));
data.attribute(MaterialAttribute(0x0));
data.attribute(MaterialAttribute(0xfefe));
data.attribute<Int>(MaterialAttribute(0x0));
data.attribute<Int>(MaterialAttribute(0xfefe));
CORRADE_COMPARE(out.str(),
"Trade::MaterialData::hasAttribute(): invalid name Trade::MaterialAttribute(0x0)\n"
"Trade::MaterialData::hasAttribute(): invalid name Trade::MaterialAttribute(0xfefe)\n"
"Trade::MaterialData::attributeId(): invalid name Trade::MaterialAttribute(0x0)\n"
"Trade::MaterialData::attributeId(): invalid name Trade::MaterialAttribute(0xfefe)\n"
"Trade::MaterialData::attributeType(): invalid name Trade::MaterialAttribute(0x0)\n"
"Trade::MaterialData::attributeType(): invalid name Trade::MaterialAttribute(0xfefe)\n"
"Trade::MaterialData::attribute(): invalid name Trade::MaterialAttribute(0x0)\n"
"Trade::MaterialData::attribute(): invalid name Trade::MaterialAttribute(0xfefe)\n"
"Trade::MaterialData::attribute(): invalid name Trade::MaterialAttribute(0x0)\n"
"Trade::MaterialData::attribute(): invalid name Trade::MaterialAttribute(0xfefe)\n");
}
void MaterialDataTest::accessNotFound() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
MaterialData data{{
{"DiffuseColor", 0xff3366aa_rgbaf}
}};
CORRADE_VERIFY(!data.hasAttribute("DiffuseColour"));
std::ostringstream out;
Error redirectError{&out};
data.attributeId("DiffuseColour");
data.attributeType("DiffuseColour");
data.attribute("DiffuseColour");
data.attribute<Color4>("DiffuseColour");
CORRADE_COMPARE(out.str(),
"Trade::MaterialData::attributeId(): attribute DiffuseColour not found\n"
"Trade::MaterialData::attributeType(): attribute DiffuseColour not found\n"
"Trade::MaterialData::attribute(): attribute DiffuseColour not found\n"
"Trade::MaterialData::attribute(): attribute DiffuseColour not found\n");
}
void MaterialDataTest::accessWrongType() {
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
MaterialData data{{
{"DiffuseColor", 0xff3366aa_rgbaf}
}};
std::ostringstream out;
Error redirectError{&out};
data.attribute<Color3>(0);
data.attribute<Color3>(MaterialAttribute::DiffuseColor);
data.attribute<Color3>("DiffuseColor");
CORRADE_COMPARE(out.str(),
"Trade::MaterialData::attribute(): improper type requested for DiffuseColor of Trade::MaterialAttributeType::Vector4\n"
"Trade::MaterialData::attribute(): improper type requested for DiffuseColor of Trade::MaterialAttributeType::Vector4\n"
"Trade::MaterialData::attribute(): improper type requested for DiffuseColor of Trade::MaterialAttributeType::Vector4\n");
}
void MaterialDataTest::release() {
MaterialData data{{
{"DiffuseColor", 0xff3366aa_rgbaf},
{MaterialAttribute::NormalTexture, 0u}
}};
const void* pointer = data.data().data();
Containers::Array<MaterialAttributeData> released = data.release();
CORRADE_COMPARE(released.data(), pointer);
CORRADE_COMPARE(released.size(), 2);
CORRADE_VERIFY(!data.data());
CORRADE_COMPARE(data.attributeCount(), 0);
}
void MaterialDataTest::constructPhong() {
using namespace Math::Literals;
@ -198,8 +900,6 @@ void MaterialDataTest::constructPhongTextureTransformNoTextures() {
}
void MaterialDataTest::constructPhongNoTextureTransformationFlag() {
using namespace Math::Literals;
#ifdef CORRADE_NO_ASSERT
CORRADE_SKIP("CORRADE_NO_ASSERT defined, can't test assertions");
#endif
@ -231,14 +931,14 @@ void MaterialDataTest::constructPhongNoTextureCoordinatesFlag() {
"PhongMaterialData::PhongMaterialData: non-zero texture coordinate sets require Flag::TextureCoordinates to be enabled\n");
}
void MaterialDataTest::constructCopy() {
void MaterialDataTest::constructPhongCopy() {
CORRADE_VERIFY(!(std::is_constructible<AbstractMaterialData, const AbstractMaterialData&>{}));
CORRADE_VERIFY(!(std::is_constructible<PhongMaterialData, const PhongMaterialData&>{}));
CORRADE_VERIFY(!(std::is_assignable<AbstractMaterialData, const AbstractMaterialData&>{}));
CORRADE_VERIFY(!(std::is_assignable<PhongMaterialData, const PhongMaterialData&>{}));
}
void MaterialDataTest::constructMovePhong() {
void MaterialDataTest::constructPhongMove() {
using namespace Math::Literals;
const int a{};
@ -333,6 +1033,20 @@ void MaterialDataTest::accessInvalidTextures() {
"Trade::PhongMaterialData::normalTextureCoordinates(): the material doesn't have a normal texture\n");
}
void MaterialDataTest::debugAttribute() {
std::ostringstream out;
Debug{&out} << MaterialAttribute::DiffuseTextureCoordinates << MaterialAttribute(0xfefe) << MaterialAttribute{};
CORRADE_COMPARE(out.str(), "Trade::MaterialAttribute::DiffuseTextureCoordinates Trade::MaterialAttribute(0xfefe) Trade::MaterialAttribute(0x0)\n");
}
void MaterialDataTest::debugAttributeType() {
std::ostringstream out;
Debug{&out} << MaterialAttributeType::Matrix3x2 << MaterialAttributeType(0xfe);
CORRADE_COMPARE(out.str(), "Trade::MaterialAttributeType::Matrix3x2 Trade::MaterialAttributeType(0xfe)\n");
}
void MaterialDataTest::debugType() {
std::ostringstream out;

4
src/Magnum/Trade/Trade.h

@ -48,9 +48,13 @@ class AbstractSceneConverter;
typedef CORRADE_DEPRECATED("use InputFileCallbackPolicy instead") InputFileCallbackPolicy ImporterFileCallbackPolicy;
#endif
enum class MaterialAttribute: UnsignedInt;
enum class MaterialAttributeType: UnsignedByte;
enum class MaterialType: UnsignedByte;
enum class MaterialAlphaMode: UnsignedByte;
class AbstractMaterialData;
class MaterialAttributeData;
class MaterialData;
enum class AnimationTrackTargetType: UnsignedByte;
enum class AnimationTrackType: UnsignedByte;

Loading…
Cancel
Save