mirror of https://github.com/mosra/magnum.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
358 lines
20 KiB
358 lines
20 KiB
|
7 years ago
|
/*
|
||
|
|
This file is part of Magnum.
|
||
|
|
|
||
|
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019
|
||
|
|
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 "MeshData.h"
|
||
|
|
|
||
|
|
#include <Corrade/Utility/Algorithms.h>
|
||
|
|
|
||
|
|
#include "Magnum/Math/Color.h"
|
||
|
|
#include "Magnum/Trade/Implementation/arrayUtilities.h"
|
||
|
|
|
||
|
|
namespace Magnum { namespace Trade {
|
||
|
|
|
||
|
|
MeshIndexData::MeshIndexData(const MeshIndexType type, const Containers::ArrayView<const void> data) noexcept: type{type}, data{reinterpret_cast<const Containers::ArrayView<const char>&>(data)} {
|
||
|
|
CORRADE_ASSERT(!data.empty(),
|
||
|
|
"Trade::MeshIndexData: index array can't be empty, create a non-indexed mesh instead", );
|
||
|
|
CORRADE_ASSERT(data.size()%meshIndexTypeSize(type) == 0,
|
||
|
|
"Trade::MeshIndexData: view size" << data.size() << "does not correspond to" << type, );
|
||
|
|
}
|
||
|
|
|
||
|
|
MeshAttributeData::MeshAttributeData(const MeshAttribute name, const VertexFormat format, const Containers::StridedArrayView1D<const char>& data) noexcept: name{name}, format{format}, data{data} {
|
||
|
|
/** @todo support zero / negative stride? would be hard to transfer to GL */
|
||
|
|
CORRADE_ASSERT(data.empty() || std::ptrdiff_t(vertexFormatSize(format)) <= data.stride(),
|
||
|
|
"Trade::MeshAttributeData: view stride" << data.stride() << "is not large enough to contain" << format, );
|
||
|
|
CORRADE_ASSERT(
|
||
|
|
(name == MeshAttribute::Position &&
|
||
|
|
(format == VertexFormat::Vector2 ||
|
||
|
|
format == VertexFormat::Vector3)) ||
|
||
|
|
(name == MeshAttribute::Normal &&
|
||
|
|
(format == VertexFormat::Vector3)) ||
|
||
|
|
(name == MeshAttribute::Color &&
|
||
|
|
(format == VertexFormat::Vector3 ||
|
||
|
|
format == VertexFormat::Vector4)) ||
|
||
|
|
(name == MeshAttribute::TextureCoordinates &&
|
||
|
|
(format == VertexFormat::Vector2)) ||
|
||
|
|
isMeshAttributeCustom(name) /* can be any format */,
|
||
|
|
"Trade::MeshAttributeData:" << format << "is not a valid format for" << name, );
|
||
|
|
}
|
||
|
|
|
||
|
|
MeshData::MeshData(const MeshPrimitive primitive, Containers::Array<char>&& indexData, const MeshIndexData& indices, Containers::Array<char>&& vertexData, Containers::Array<MeshAttributeData>&& attributes, const void* const importerState) noexcept: _indexType{indices.type}, _primitive{primitive}, _importerState{importerState}, _indexData{std::move(indexData)}, _vertexData{std::move(vertexData)}, _attributes{std::move(attributes)}, _indices{indices.data} {
|
||
|
|
/* Save vertex count. It's a strided array view, so the size is not
|
||
|
|
depending on type. */
|
||
|
|
if(_attributes.empty()) {
|
||
|
|
CORRADE_ASSERT(indices.type != MeshIndexType{},
|
||
|
|
"Trade::MeshData: indices are expected to be valid if there are no attributes and vertex count isn't passed explicitly", );
|
||
|
|
/** @todo some better value? attributeless indexed with defined vertex count? */
|
||
|
|
_vertexCount = 0;
|
||
|
|
} else _vertexCount = _attributes[0].data.size();
|
||
|
|
|
||
|
|
CORRADE_ASSERT(!_indices.empty() || !_indexData,
|
||
|
|
"Trade::MeshData: indexData passed for a non-indexed mesh", );
|
||
|
|
CORRADE_ASSERT(_indices.empty() || (_indices.begin() >= _indexData.begin() && _indices.end() <= _indexData.end()),
|
||
|
|
"Trade::MeshData: indices [" << Debug::nospace << static_cast<const void*>(_indices.begin()) << Debug::nospace << ":" << Debug::nospace << static_cast<const void*>(_indices.end()) << Debug::nospace << "] are not contained in passed indexData array [" << Debug::nospace << static_cast<const void*>(_indexData.begin()) << Debug::nospace << ":" << Debug::nospace << static_cast<const void*>(_indexData.end()) << Debug::nospace << "]", );
|
||
|
|
CORRADE_ASSERT(!_attributes.empty() || !_vertexData,
|
||
|
|
"Trade::MeshData: vertexData passed for an attribute-less mesh", );
|
||
|
|
CORRADE_ASSERT(_vertexCount || !_vertexData,
|
||
|
|
"Trade::MeshData: vertexData passed for a mesh with zero vertices", );
|
||
|
|
|
||
|
|
#ifndef CORRADE_NO_ASSERT
|
||
|
|
/* Not checking what's already checked in MeshIndexData / MeshAttributeData
|
||
|
|
constructors */
|
||
|
|
for(std::size_t i = 0; i != _attributes.size(); ++i) {
|
||
|
|
const MeshAttributeData& attribute = _attributes[i];
|
||
|
|
CORRADE_ASSERT(attribute.data.size() == _vertexCount,
|
||
|
|
"Trade::MeshData: attribute" << i << "has" << attribute.data.size() << "vertices but" << _vertexCount << "expected", );
|
||
|
|
CORRADE_ASSERT(attribute.data.empty() || (&attribute.data.front() >= _vertexData.begin() && &attribute.data.back() + vertexFormatSize(attribute.format) <= _vertexData.end()),
|
||
|
|
"Trade::MeshData: attribute" << i << "[" << Debug::nospace << static_cast<const void*>(&attribute.data.front()) << Debug::nospace << ":" << Debug::nospace << static_cast<const void*>(&attribute.data.back() + vertexFormatSize(attribute.format)) << Debug::nospace << "] is not contained in passed vertexData array [" << Debug::nospace << static_cast<const void*>(_vertexData.begin()) << Debug::nospace << ":" << Debug::nospace << static_cast<const void*>(_vertexData.end()) << Debug::nospace << "]", );
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
MeshData::MeshData(const MeshPrimitive primitive, Containers::Array<char>&& indexData, const MeshIndexData& indices, Containers::Array<char>&& vertexData, std::initializer_list<MeshAttributeData> attributes, const void* const importerState): MeshData{primitive, std::move(indexData), indices, std::move(vertexData), Implementation::initializerListToArrayWithDefaultDeleter(attributes), importerState} {}
|
||
|
|
|
||
|
|
MeshData::MeshData(const MeshPrimitive primitive, Containers::Array<char>&& vertexData, Containers::Array<MeshAttributeData>&& attributes, const void* const importerState) noexcept: MeshData{primitive, {}, MeshIndexData{}, std::move(vertexData), std::move(attributes), importerState} {}
|
||
|
|
|
||
|
|
MeshData::MeshData(const MeshPrimitive primitive, Containers::Array<char>&& vertexData, const std::initializer_list<MeshAttributeData> attributes, const void* const importerState): MeshData{primitive, std::move(vertexData), Implementation::initializerListToArrayWithDefaultDeleter(attributes), importerState} {}
|
||
|
|
|
||
|
|
MeshData::MeshData(const MeshPrimitive primitive, Containers::Array<char>&& indexData, const MeshIndexData& indices, const void* const importerState) noexcept: MeshData{primitive, std::move(indexData), indices, {}, {}, importerState} {}
|
||
|
|
|
||
|
|
MeshData::MeshData(const MeshPrimitive primitive, const UnsignedInt vertexCount, const void* const importerState) noexcept: _vertexCount{vertexCount}, _indexType{}, _primitive{primitive}, _importerState{importerState} {}
|
||
|
|
|
||
|
|
MeshData::~MeshData() = default;
|
||
|
|
|
||
|
|
MeshData::MeshData(MeshData&&) noexcept = default;
|
||
|
|
|
||
|
|
MeshData& MeshData::operator=(MeshData&&) noexcept = default;
|
||
|
|
|
||
|
|
UnsignedInt MeshData::indexCount() const {
|
||
|
|
CORRADE_ASSERT(isIndexed(),
|
||
|
|
"Trade::MeshData::indexCount(): the mesh is not indexed", {});
|
||
|
|
return _indices.size()/meshIndexTypeSize(_indexType);
|
||
|
|
}
|
||
|
|
|
||
|
|
MeshIndexType MeshData::indexType() const {
|
||
|
|
CORRADE_ASSERT(isIndexed(),
|
||
|
|
"Trade::MeshData::indexType(): the mesh is not indexed", {});
|
||
|
|
return _indexType;
|
||
|
|
}
|
||
|
|
|
||
|
|
MeshAttribute MeshData::attributeName(UnsignedInt id) const {
|
||
|
|
CORRADE_ASSERT(id < _attributes.size(),
|
||
|
|
"Trade::MeshData::attributeName(): index" << id << "out of range for" << _attributes.size() << "attributes", {});
|
||
|
|
return _attributes[id].name;
|
||
|
|
}
|
||
|
|
|
||
|
|
VertexFormat MeshData::attributeFormat(UnsignedInt id) const {
|
||
|
|
CORRADE_ASSERT(id < _attributes.size(),
|
||
|
|
"Trade::MeshData::attributeFormat(): index" << id << "out of range for" << _attributes.size() << "attributes", {});
|
||
|
|
return _attributes[id].format;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::size_t MeshData::attributeOffset(UnsignedInt id) const {
|
||
|
|
CORRADE_ASSERT(id < _attributes.size(),
|
||
|
|
"Trade::MeshData::attributeOffset(): index" << id << "out of range for" << _attributes.size() << "attributes", {});
|
||
|
|
return static_cast<const char*>(_attributes[id].data.data()) - _vertexData.data();
|
||
|
|
}
|
||
|
|
|
||
|
|
UnsignedInt MeshData::attributeStride(UnsignedInt id) const {
|
||
|
|
CORRADE_ASSERT(id < _attributes.size(),
|
||
|
|
"Trade::MeshData::attributeStride(): index" << id << "out of range for" << _attributes.size() << "attributes", {});
|
||
|
|
return _attributes[id].data.stride();
|
||
|
|
}
|
||
|
|
|
||
|
|
UnsignedInt MeshData::attributeCount(const MeshAttribute name) const {
|
||
|
|
UnsignedInt count = 0;
|
||
|
|
for(const MeshAttributeData& attribute: _attributes)
|
||
|
|
if(attribute.name == name) ++count;
|
||
|
|
return count;
|
||
|
|
}
|
||
|
|
|
||
|
|
UnsignedInt MeshData::attributeFor(const MeshAttribute name, UnsignedInt id) const {
|
||
|
|
for(std::size_t i = 0; i != _attributes.size(); ++i) {
|
||
|
|
if(_attributes[i].name != name) continue;
|
||
|
|
if(id-- == 0) return i;
|
||
|
|
}
|
||
|
|
|
||
|
|
#ifdef CORRADE_NO_ASSERT
|
||
|
|
CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
|
||
|
|
#else
|
||
|
|
return ~UnsignedInt{};
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
VertexFormat MeshData::attributeFormat(MeshAttribute name, UnsignedInt id) const {
|
||
|
|
const UnsignedInt attributeId = attributeFor(name, id);
|
||
|
|
CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::attributeFormat(): index" << id << "out of range for" << attributeCount(name) << name << "attributes", {});
|
||
|
|
return attributeFormat(attributeId);
|
||
|
|
}
|
||
|
|
|
||
|
|
std::size_t MeshData::attributeOffset(MeshAttribute name, UnsignedInt id) const {
|
||
|
|
const UnsignedInt attributeId = attributeFor(name, id);
|
||
|
|
CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::attributeOffset(): index" << id << "out of range for" << attributeCount(name) << name << "attributes", {});
|
||
|
|
return attributeOffset(attributeId);
|
||
|
|
}
|
||
|
|
|
||
|
|
UnsignedInt MeshData::attributeStride(MeshAttribute name, UnsignedInt id) const {
|
||
|
|
const UnsignedInt attributeId = attributeFor(name, id);
|
||
|
|
CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::attributeStride(): index" << id << "out of range for" << attributeCount(name) << name << "attributes", {});
|
||
|
|
return attributeStride(attributeId);
|
||
|
|
}
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
template<class T> void convertIndices(const Containers::ArrayView<const char> data, const Containers::ArrayView<UnsignedInt> destination) {
|
||
|
|
const auto input = Containers::arrayCast<const T>(data);
|
||
|
|
for(std::size_t i = 0; i != input.size(); ++i) destination[i] = input[i];
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
void MeshData::indicesInto(const Containers::ArrayView<UnsignedInt> destination) const {
|
||
|
|
CORRADE_ASSERT(isIndexed(),
|
||
|
|
"Trade::MeshData::indicesInto(): the mesh is not indexed", );
|
||
|
|
CORRADE_ASSERT(destination.size() == indexCount(), "Trade::MeshData::indicesInto(): expected a view with" << indexCount() << "elements but got" << destination.size(), );
|
||
|
|
|
||
|
|
switch(_indexType) {
|
||
|
|
case MeshIndexType::UnsignedByte: return convertIndices<UnsignedByte>(_indices, destination);
|
||
|
|
case MeshIndexType::UnsignedShort: return convertIndices<UnsignedShort>(_indices, destination);
|
||
|
|
case MeshIndexType::UnsignedInt: return convertIndices<UnsignedInt>(_indices, destination);
|
||
|
|
}
|
||
|
|
|
||
|
|
CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
|
||
|
|
}
|
||
|
|
|
||
|
|
Containers::Array<UnsignedInt> MeshData::indicesAsArray() const {
|
||
|
|
/* Repeating the assert here because otherwise it would fire in
|
||
|
|
indexCount() which may be confusing */
|
||
|
|
CORRADE_ASSERT(isIndexed(), "Trade::MeshData::indicesAsArray(): the mesh is not indexed", {});
|
||
|
|
Containers::Array<UnsignedInt> output{indexCount()};
|
||
|
|
indicesInto(output);
|
||
|
|
return output;
|
||
|
|
}
|
||
|
|
|
||
|
|
void MeshData::positions2DInto(const Containers::StridedArrayView1D<Vector2> destination, const UnsignedInt id) const {
|
||
|
|
const UnsignedInt attributeId = attributeFor(MeshAttribute::Position, id);
|
||
|
|
CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::positions2DInto(): index" << id << "out of range for" << attributeCount(MeshAttribute::Position) << "position attributes", );
|
||
|
|
CORRADE_ASSERT(destination.size() == _vertexCount, "Trade::MeshData::positions2DInto(): expected a view with" << _vertexCount << "elements but got" << destination.size(), );
|
||
|
|
const MeshAttributeData& attribute = _attributes[attributeId];
|
||
|
|
|
||
|
|
/* Copy 2D positions as-is, for 3D positions ignore Z */
|
||
|
|
if(attribute.format == VertexFormat::Vector2 ||
|
||
|
|
attribute.format == VertexFormat::Vector3)
|
||
|
|
Utility::copy(Containers::arrayCast<const Vector2>(attribute.data), destination);
|
||
|
|
else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
|
||
|
|
}
|
||
|
|
|
||
|
|
Containers::Array<Vector2> MeshData::positions2DAsArray(const UnsignedInt id) const {
|
||
|
|
Containers::Array<Vector2> out{_vertexCount};
|
||
|
|
positions2DInto(out, id);
|
||
|
|
return out;
|
||
|
|
}
|
||
|
|
|
||
|
|
void MeshData::positions3DInto(const Containers::StridedArrayView1D<Vector3> destination, const UnsignedInt id) const {
|
||
|
|
const UnsignedInt attributeId = attributeFor(MeshAttribute::Position, id);
|
||
|
|
CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::positions3DInto(): index" << id << "out of range for" << attributeCount(MeshAttribute::Position) << "position attributes", );
|
||
|
|
CORRADE_ASSERT(destination.size() == _vertexCount, "Trade::MeshData::positions3DInto(): expected a view with" << _vertexCount << "elements but got" << destination.size(), );
|
||
|
|
const MeshAttributeData& attribute = _attributes[attributeId];
|
||
|
|
|
||
|
|
/* For 2D positions copy the XY part to the first two components and then
|
||
|
|
fill the Z with a single value */
|
||
|
|
if(attribute.format == VertexFormat::Vector2) {
|
||
|
|
Utility::copy(Containers::arrayCast<const Vector2>(attribute.data),
|
||
|
|
Containers::arrayCast<Vector2>(destination));
|
||
|
|
constexpr Float z[1]{0.0f};
|
||
|
|
Utility::copy(
|
||
|
|
Containers::stridedArrayView(z).broadcasted<0>(_vertexCount),
|
||
|
|
Containers::arrayCast<2, Float>(destination).transposed<0, 1>()[2]);
|
||
|
|
/* Copy 3D positions as-is */
|
||
|
|
} else if(attribute.format == VertexFormat::Vector3) {
|
||
|
|
Utility::copy(Containers::arrayCast<const Vector3>(attribute.data), destination);
|
||
|
|
} else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
|
||
|
|
}
|
||
|
|
|
||
|
|
Containers::Array<Vector3> MeshData::positions3DAsArray(const UnsignedInt id) const {
|
||
|
|
Containers::Array<Vector3> out{_vertexCount};
|
||
|
|
positions3DInto(out, id);
|
||
|
|
return out;
|
||
|
|
}
|
||
|
|
|
||
|
|
void MeshData::normalsInto(const Containers::StridedArrayView1D<Vector3> destination, const UnsignedInt id) const {
|
||
|
|
const UnsignedInt attributeId = attributeFor(MeshAttribute::Normal, id);
|
||
|
|
CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::normalsInto(): index" << id << "out of range for" << attributeCount(MeshAttribute::Normal) << "normal attributes", );
|
||
|
|
CORRADE_ASSERT(destination.size() == _vertexCount, "Trade::MeshData::normalsInto(): expected a view with" << _vertexCount << "elements but got" << destination.size(), );
|
||
|
|
const MeshAttributeData& attribute = _attributes[attributeId];
|
||
|
|
|
||
|
|
if(attribute.format == VertexFormat::Vector3)
|
||
|
|
Utility::copy(Containers::arrayCast<const Vector3>(attribute.data), destination);
|
||
|
|
else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
|
||
|
|
}
|
||
|
|
|
||
|
|
Containers::Array<Vector3> MeshData::normalsAsArray(const UnsignedInt id) const {
|
||
|
|
Containers::Array<Vector3> out{_vertexCount};
|
||
|
|
normalsInto(out, id);
|
||
|
|
return out;
|
||
|
|
}
|
||
|
|
|
||
|
|
void MeshData::textureCoordinates2DInto(const Containers::StridedArrayView1D<Vector2> destination, const UnsignedInt id) const {
|
||
|
|
const UnsignedInt attributeId = attributeFor(MeshAttribute::TextureCoordinates, id);
|
||
|
|
CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::textureCoordinates2DInto(): index" << id << "out of range for" << attributeCount(MeshAttribute::TextureCoordinates) << "texture coordinate attributes", );
|
||
|
|
CORRADE_ASSERT(destination.size() == _vertexCount, "Trade::MeshData::textureCoordinates2DInto(): expected a view with" << _vertexCount << "elements but got" << destination.size(), );
|
||
|
|
const MeshAttributeData& attribute = _attributes[attributeId];
|
||
|
|
|
||
|
|
if(attribute.format == VertexFormat::Vector2)
|
||
|
|
Utility::copy(Containers::arrayCast<const Vector2>(attribute.data), destination);
|
||
|
|
else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
|
||
|
|
}
|
||
|
|
|
||
|
|
Containers::Array<Vector2> MeshData::textureCoordinates2DAsArray(const UnsignedInt id) const {
|
||
|
|
Containers::Array<Vector2> out{_vertexCount};
|
||
|
|
textureCoordinates2DInto(out, id);
|
||
|
|
return out;
|
||
|
|
}
|
||
|
|
|
||
|
|
void MeshData::colorsInto(const Containers::StridedArrayView1D<Color4> destination, const UnsignedInt id) const {
|
||
|
|
const UnsignedInt attributeId = attributeFor(MeshAttribute::Color, id);
|
||
|
|
CORRADE_ASSERT(attributeId != ~UnsignedInt{}, "Trade::MeshData::colorsInto(): index" << id << "out of range for" << attributeCount(MeshAttribute::Color) << "color attributes", );
|
||
|
|
CORRADE_ASSERT(destination.size() == _vertexCount, "Trade::MeshData::colorsInto(): expected a view with" << _vertexCount << "elements but got" << destination.size(), );
|
||
|
|
const MeshAttributeData& attribute = _attributes[attributeId];
|
||
|
|
|
||
|
|
/* For three-component colors copy the RGB part to the first three
|
||
|
|
components and then fill the alpha with a single value */
|
||
|
|
if(attribute.format == VertexFormat::Vector3) {
|
||
|
|
Utility::copy(Containers::arrayCast<const Vector3>(attribute.data),
|
||
|
|
Containers::arrayCast<Vector3>(destination));
|
||
|
|
constexpr Float alpha[1]{1.0f};
|
||
|
|
Utility::copy(
|
||
|
|
Containers::stridedArrayView(alpha).broadcasted<0>(_vertexCount),
|
||
|
|
Containers::arrayCast<2, Float>(destination).transposed<0, 1>()[3]);
|
||
|
|
/* Copy four-component colors as-is */
|
||
|
|
} else if(attribute.format == VertexFormat::Vector4) {
|
||
|
|
Utility::copy(Containers::arrayCast<const Vector4>(attribute.data),
|
||
|
|
Containers::arrayCast<Vector4>(destination));
|
||
|
|
} else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
|
||
|
|
}
|
||
|
|
|
||
|
|
Containers::Array<Color4> MeshData::colorsAsArray(const UnsignedInt id) const {
|
||
|
|
Containers::Array<Color4> out{_vertexCount};
|
||
|
|
colorsInto(out, id);
|
||
|
|
return out;
|
||
|
|
}
|
||
|
|
|
||
|
|
Containers::Array<char> MeshData::releaseIndexData() {
|
||
|
|
_indexType = MeshIndexType{}; /* so isIndexed() returns false */
|
||
|
|
_indices = nullptr;
|
||
|
|
return std::move(_indexData);
|
||
|
|
}
|
||
|
|
|
||
|
|
Containers::Array<char> MeshData::releaseVertexData() {
|
||
|
|
_attributes = nullptr;
|
||
|
|
return std::move(_vertexData);
|
||
|
|
}
|
||
|
|
|
||
|
|
Debug& operator<<(Debug& debug, const MeshAttribute value) {
|
||
|
|
debug << "Trade::MeshAttribute" << Debug::nospace;
|
||
|
|
|
||
|
|
if(UnsignedShort(value) >= UnsignedShort(MeshAttribute::Custom))
|
||
|
|
return debug << "::Custom(" << Debug::nospace << (UnsignedShort(value) - UnsignedShort(MeshAttribute::Custom)) << Debug::nospace << ")";
|
||
|
|
|
||
|
|
switch(value) {
|
||
|
|
/* LCOV_EXCL_START */
|
||
|
|
#define _c(value) case MeshAttribute::value: return debug << "::" << Debug::nospace << #value;
|
||
|
|
_c(Position)
|
||
|
|
_c(Normal)
|
||
|
|
_c(TextureCoordinates)
|
||
|
|
_c(Color)
|
||
|
|
#undef _c
|
||
|
|
/* LCOV_EXCL_STOP */
|
||
|
|
|
||
|
|
/* To silence compiler warning about unhandled values */
|
||
|
|
case MeshAttribute::Custom: CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
|
||
|
|
}
|
||
|
|
|
||
|
|
return debug << "(" << Debug::nospace << reinterpret_cast<void*>(UnsignedShort(value)) << Debug::nospace << ")";
|
||
|
|
}
|
||
|
|
|
||
|
|
}}
|