Browse Source

MeshTools: implement compile() taking a MeshData.

Also add new variants that allow for external buffers.
pull/371/head
Vladimír Vondruš 7 years ago
parent
commit
16c3480d7f
  1. 2
      doc/changelog.dox
  2. 33
      doc/snippets/MagnumMeshTools-gl.cpp
  3. 4
      src/Magnum/MeshTools/CMakeLists.txt
  4. 148
      src/Magnum/MeshTools/Compile.cpp
  5. 91
      src/Magnum/MeshTools/Compile.h
  6. 2
      src/Magnum/MeshTools/Test/CMakeLists.txt
  7. 414
      src/Magnum/MeshTools/Test/CompileGLTest.cpp

2
doc/changelog.dox

@ -109,6 +109,8 @@ See also:
@subsubsection changelog-latest-new-meshtools MeshTools library
- Added @ref MeshTools::compile(const Trade::MeshData&) operating on the new
@ref Trade::MeshData API
- New @ref MeshTools::isInterleaved() utility for checking if
@ref Trade::MeshData is interleaved
- Added @ref MeshTools::interleavedLayout() for convenient creation of an

33
doc/snippets/MagnumMeshTools-gl.cpp

@ -29,13 +29,46 @@
#include "Magnum/GL/Buffer.h"
#include "Magnum/GL/Mesh.h"
#include "Magnum/Math/Vector3.h"
#include "Magnum/MeshTools/Compile.h"
#include "Magnum/MeshTools/CompressIndices.h"
#include "Magnum/MeshTools/Interleave.h"
#include "Magnum/Trade/MeshData.h"
using namespace Magnum;
int main() {
{
Trade::MeshData meshData{MeshPrimitive::Lines, 5};
/* [compile-external] */
GL::Buffer indices, vertices;
indices.setData(meshData.indexData());
vertices.setData(meshData.vertexData());
GL::Mesh mesh = MeshTools::compile(meshData, indices, vertices);
/* [compile-external] */
}
{
Trade::MeshData meshData{MeshPrimitive::Lines, 5};
Trade::MeshAttribute myCustomAttribute{};
/* [compile-external-attributes] */
GL::Buffer indices, vertices;
indices.setData(meshData.indexData());
vertices.setData(meshData.vertexData());
/* Let compile() handle the usual attributes and configure custom ones after */
GL::Mesh mesh = MeshTools::compile(meshData, std::move(indices), vertices);
mesh.addVertexBuffer(std::move(vertices),
meshData.attributeOffset(myCustomAttribute),
meshData.attributeStride(myCustomAttribute),
GL::DynamicAttribute{
GL::DynamicAttribute::Kind::Generic, 7,
GL::DynamicAttribute::Components::One,
GL::DynamicAttribute::DataType::Float});
/* [compile-external-attributes] */
}
{
/* [compressIndices] */
Containers::Array<UnsignedInt> indices;

4
src/Magnum/MeshTools/CMakeLists.txt

@ -60,9 +60,11 @@ endif()
if(TARGET_GL)
list(APPEND MagnumMeshTools_SRCS
Compile.cpp
FullScreenTriangle.cpp)
list(APPEND MagnumMeshTools_GracefulAssert_SRCS
Compile.cpp)
list(APPEND MagnumMeshTools_HEADERS
Compile.h
FullScreenTriangle.h)

148
src/Magnum/MeshTools/Compile.cpp

@ -25,8 +25,9 @@
#include "Compile.h"
#include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/StridedArrayView.h>
#include <Corrade/Containers/ArrayViewStl.h> /** @todo remove once MeshData is sane */
#include <Corrade/Containers/ArrayViewStl.h> /** @todo remove once MeshDataXD is gone */
#include "Magnum/GL/Buffer.h"
#include "Magnum/GL/Mesh.h"
@ -36,6 +37,7 @@
#include "Magnum/MeshTools/GenerateNormals.h"
#include "Magnum/MeshTools/Duplicate.h"
#include "Magnum/MeshTools/Interleave.h"
#include "Magnum/Trade/MeshData.h"
#include "Magnum/Trade/MeshData2D.h"
#include "Magnum/Trade/MeshData3D.h"
@ -45,6 +47,150 @@
namespace Magnum { namespace MeshTools {
GL::Mesh compile(const Trade::MeshData& meshData, CompileFlags flags) {
/* If we want to generate normals, prepare a new mesh data and recurse,
with the flags unset */
if(meshData.primitive() == MeshPrimitive::Triangles && (flags & (CompileFlag::GenerateFlatNormals|CompileFlag::GenerateSmoothNormals))) {
CORRADE_ASSERT(meshData.attributeCount(Trade::MeshAttribute::Position),
"MeshTools::compile(): the mesh has no positions, can't generate normals", GL::Mesh{});
/* Right now this could fire only if we have 2D positions, which is
unlikely; in the future it might fire once packed formats are added */
CORRADE_ASSERT(meshData.attributeFormat(Trade::MeshAttribute::Position) == VertexFormat::Vector3,
"MeshTools::compile(): can't generate normals for" << meshData.attributeFormat(Trade::MeshAttribute::Position) << "positions", GL::Mesh{});
/* If the data already have a normal array, reuse its location,
otherwise mix in an extra one */
Trade::MeshAttributeData normalAttribute;
Containers::ArrayView<const Trade::MeshAttributeData> extra;
if(!meshData.hasAttribute(Trade::MeshAttribute::Normal)) {
normalAttribute = Trade::MeshAttributeData{
Trade::MeshAttribute::Normal, VertexFormat::Vector3,
nullptr};
extra = {&normalAttribute, 1};
/* If we reuse a normal location, expect correct type. Again this won't
fire now, but might in the future once packed formats are added */
} else CORRADE_ASSERT(meshData.attributeFormat(Trade::MeshAttribute::Normal) == VertexFormat::Vector3,
"MeshTools::compile(): can't generate normals into" << meshData.attributeFormat(Trade::MeshAttribute::Normal), GL::Mesh{});
/* If we want flat normals, we need to first duplicate everything using
the index buffer. Otherwise just interleave the potential extra
normal attribute in. */
Trade::MeshData generated{MeshPrimitive::Points, 0};
if(flags & CompileFlag::GenerateFlatNormals && meshData.isIndexed())
generated = duplicate(meshData, extra);
else
generated = interleave(meshData, extra);
/* Generate the normals. If we don't have the index buffer, we can only
generate flat ones. */
if(flags & CompileFlag::GenerateFlatNormals || !meshData.isIndexed())
generateFlatNormalsInto(
generated.attribute<Vector3>(Trade::MeshAttribute::Position),
generated.mutableAttribute<Vector3>(Trade::MeshAttribute::Normal));
else
generateSmoothNormalsInto(generated.indices(),
generated.attribute<Vector3>(Trade::MeshAttribute::Position),
generated.mutableAttribute<Vector3>(Trade::MeshAttribute::Normal));
return compile(generated, flags & ~(CompileFlag::GenerateFlatNormals|CompileFlag::GenerateSmoothNormals));
}
flags &= ~(CompileFlag::GenerateFlatNormals|CompileFlag::GenerateSmoothNormals);
CORRADE_INTERNAL_ASSERT(!flags);
return compile(meshData);
}
GL::Mesh compile(const Trade::MeshData& meshData) {
GL::Buffer indices{NoCreate};
if(meshData.isIndexed()) {
indices = GL::Buffer{GL::Buffer::TargetHint::ElementArray};
indices.setData(meshData.indexData());
}
GL::Buffer vertices{GL::Buffer::TargetHint::Array};
vertices.setData(meshData.vertexData());
return compile(meshData, std::move(indices), std::move(vertices));
}
GL::Mesh compile(const Trade::MeshData& meshData, GL::Buffer& indices, GL::Buffer& vertices) {
return compile(meshData, GL::Buffer::wrap(indices.id(), GL::Buffer::TargetHint::ElementArray), GL::Buffer::wrap(vertices.id(), GL::Buffer::TargetHint::Array));
}
GL::Mesh compile(const Trade::MeshData& meshData, GL::Buffer& indices, GL::Buffer&& vertices) {
return compile(meshData, GL::Buffer::wrap(indices.id(), GL::Buffer::TargetHint::ElementArray), std::move(vertices));
}
GL::Mesh compile(const Trade::MeshData& meshData, GL::Buffer&& indices, GL::Buffer& vertices) {
return compile(meshData, std::move(indices), GL::Buffer::wrap(vertices.id(), GL::Buffer::TargetHint::Array));
}
GL::Mesh compile(const Trade::MeshData& meshData, GL::Buffer&& indices, GL::Buffer&& vertices) {
CORRADE_ASSERT((!meshData.isIndexed() || indices.id()) && vertices.id(),
"MeshTools::compile(): invalid external buffer(s)", GL::Mesh{});
/* Basics */
GL::Mesh mesh;
mesh.setPrimitive(meshData.primitive());
/* Vertex data */
GL::Buffer verticesRef = GL::Buffer::wrap(vertices.id(), GL::Buffer::TargetHint::Array);
for(UnsignedInt i = 0; i != meshData.attributeCount(); ++i) {
Containers::Optional<GL::DynamicAttribute> attribute;
switch(meshData.attributeName(i)) {
case Trade::MeshAttribute::Position:
if(meshData.attributeFormat(i) == VertexFormat::Vector2)
attribute.emplace(Shaders::Generic2D::Position{});
else if(meshData.attributeFormat(i) == VertexFormat::Vector3)
attribute.emplace(Shaders::Generic3D::Position{});
else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
break;
case Trade::MeshAttribute::Normal:
CORRADE_INTERNAL_ASSERT(meshData.attributeFormat(i) == VertexFormat::Vector3);
attribute.emplace(Shaders::Generic3D::Normal{});
break;
case Trade::MeshAttribute::TextureCoordinates:
CORRADE_INTERNAL_ASSERT(meshData.attributeFormat(i) == VertexFormat::Vector2);
/** @todo have Generic2D derived from Generic that has all
attribute definitions common for 2D and 3D */
attribute.emplace(Shaders::Generic2D::TextureCoordinates{});
break;
case Trade::MeshAttribute::Color:
/** @todo have Generic2D derived from Generic that has all
attribute definitions common for 2D and 3D */
if(meshData.attributeFormat(i) == VertexFormat::Vector3)
attribute.emplace(Shaders::Generic2D::Color3{});
else if(meshData.attributeFormat(i) == VertexFormat::Vector4)
attribute.emplace(Shaders::Generic2D::Color4{});
else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
break;
/* So it doesn't yell that we didn't handle a known attribute */
case Trade::MeshAttribute::Custom: break; /* LCOV_EXCL_LINE */
}
if(!attribute) {
Warning{} << "MeshTools::compile(): ignoring unknown attribute" << meshData.attributeName(i);
continue;
}
/* For the first attribute move the buffer in, for all others use the
reference */
if(vertices.id()) mesh.addVertexBuffer(std::move(vertices),
meshData.attributeOffset(i), meshData.attributeStride(i),
*attribute);
else mesh.addVertexBuffer(verticesRef, meshData.attributeOffset(i),
meshData.attributeStride(i), *attribute);
}
if(meshData.isIndexed()) {
mesh.setIndexBuffer(std::move(indices), 0, meshData.indexType())
.setCount(meshData.indexCount());
} else mesh.setCount(meshData.vertexCount());
return mesh;
}
GL::Mesh compile(const Trade::MeshData2D& meshData) {
GL::Mesh mesh;
mesh.setPrimitive(meshData.primitive());

91
src/Magnum/MeshTools/Compile.h

@ -85,6 +85,97 @@ typedef Containers::EnumSet<CompileFlag> CompileFlags;
CORRADE_ENUMSET_OPERATORS(CompileFlags)
/**
@brief Compile mesh data
@m_since_latest
Configures a mesh for a @ref Shaders::Generic shader with a vertex buffer and
possibly also an index buffer, if the mesh is indexed.
- If the mesh contains positions, these are bound to the
@ref Shaders::Generic2D::Position attribute if they are 2D or to
@ref Shaders::Generic3D::Position if they are 3D.
- If the mesh contains normals or if @ref CompileFlag::GenerateFlatNormals /
@ref CompileFlag::GenerateSmoothNormals is set, these are bound to
@ref Shaders::Generic3D::Normal.
- If the mesh contains texture coordinates, these are bound to
@ref Shaders::Generic::TextureCoordinates.
- If the mesh contains colors, these are bound to
@ref Shaders::Generic::Color3 / @ref Shaders::Generic::Color4 based on
their type.
If normal generation is not requested, @ref Trade::MeshData::indexData() and
@ref Trade::MeshData::vertexData() are uploaded as-is without any further
modifications, keeping the original layout and vertex formats. If
@ref CompileFlag::GenerateSmoothNormals is requested, vertex data is
interleaved together with the generated normals; if
@ref CompileFlag::GenerateFlatNormals is requested, the mesh is first
deindexed and then the vertex data is interleaved together with the generated
normals.
The generated mesh owns the index and vertex buffers and there's no possibility
to access them afterwards. For alternative solutions see the
@ref compile(const Trade::MeshData&, GL::Buffer&, GL::Buffer&) overloads.
@note This function is available only if Magnum is compiled with
@ref MAGNUM_TARGET_GL enabled (done by default). See @ref building-features
for more information.
@see @ref shaders-generic
*/
MAGNUM_MESHTOOLS_EXPORT GL::Mesh compile(const Trade::MeshData& meshData, CompileFlags flags);
/**
* @overload
* @m_since_latest
*/
/* Separately because this one doesn't rely on duplicate() / interleave() /
generate*Normals() and thus the exe can be smaller when using this function
directly */
MAGNUM_MESHTOOLS_EXPORT GL::Mesh compile(const Trade::MeshData& meshData);
/**
@brief Compile mesh data using external buffers
@m_since_latest
Assumes the whole vertex / index data are already uploaded to @p indices /
@p vertices and sets up the mesh using those. Can be used to have a single
index/vertex buffer when multiple @ref Trade::MeshData instances share the same
data arrays, or to allow buffer access later. For example:
@snippet MagnumMeshTools-gl.cpp compile-external
Another use case is specifying additional vertex attributes that are not
recognized by the function itself. You can choose among various r-value
overloads depending on whether you want to have the index/vertex buffers owned
by the mesh or not:
@snippet MagnumMeshTools-gl.cpp compile-external-attributes
If @p meshData is not indexed, the @p indices parameter is ignored --- in that
case you can pass a @ref NoCreate "NoCreate"-d instance to avoid allocating an
unnecessary OpenGL buffer object.
*/
MAGNUM_MESHTOOLS_EXPORT GL::Mesh compile(const Trade::MeshData& meshData, GL::Buffer& indices, GL::Buffer& vertices);
/**
* @overload
* @m_since_latest
*/
MAGNUM_MESHTOOLS_EXPORT GL::Mesh compile(const Trade::MeshData& meshData, GL::Buffer& indices, GL::Buffer&& vertices);
/**
* @overload
* @m_since_latest
*/
MAGNUM_MESHTOOLS_EXPORT GL::Mesh compile(const Trade::MeshData& meshData, GL::Buffer&& indices, GL::Buffer& vertices);
/**
* @overload
* @m_since_latest
*/
MAGNUM_MESHTOOLS_EXPORT GL::Mesh compile(const Trade::MeshData& meshData, GL::Buffer&& indices, GL::Buffer&& vertices);
/**
@brief Compile 2D mesh data

2
src/Magnum/MeshTools/Test/CMakeLists.txt

@ -100,7 +100,7 @@ if(BUILD_GL_TESTS)
MagnumDebugTools
MagnumGL
MagnumOpenGLTester
MagnumMeshTools
MagnumMeshToolsTestLib
MagnumShaders
FILES
CompileTestFiles/color2D.tga

414
src/Magnum/MeshTools/Test/CompileGLTest.cpp

@ -23,9 +23,12 @@
DEALINGS IN THE SOFTWARE.
*/
#include <sstream>
#include <Corrade/Containers/EnumSet.h>
#include <Corrade/Containers/GrowableArray.h>
#include <Corrade/PluginManager/Manager.h>
#include <Corrade/Utility/Directory.h>
#include <Corrade/Utility/DebugStl.h>
#include "Magnum/Image.h"
#include "Magnum/ImageView.h"
@ -78,8 +81,14 @@ struct CompileGLTest: GL::OpenGLTester {
public:
explicit CompileGLTest();
void twoDimensions();
void threeDimensions();
template<class T> void twoDimensions();
template<class T> void threeDimensions();
void unknownAttribute();
void generateNormalsNoPosition();
void generateNormals2DPosition();
void externalBuffers();
void externalBuffersInvalid();
private:
PluginManager::Manager<Trade::AbstractImporter> _manager{"nonexistent"};
@ -138,6 +147,17 @@ constexpr struct {
{"positions, nonindexed + gen smooth normals", Flag::NonIndexed|Flag::GeneratedSmoothNormals},
};
constexpr struct {
const char* name;
bool indexed, moveIndices, moveVertices;
} DataExternal[] {
{"indexed", true, false, false},
{"", false, false, false},
{"move indices", true, true, false},
{"move vertices", false, false, true},
{"move both", true, true, true}
};
using namespace Math::Literals;
constexpr Color4ub ImageData[] {
@ -148,11 +168,24 @@ constexpr Color4ub ImageData[] {
};
CompileGLTest::CompileGLTest() {
addInstancedTests({&CompileGLTest::twoDimensions},
Containers::arraySize(Data2D));
addInstancedTests<CompileGLTest>({
&CompileGLTest::twoDimensions<Trade::MeshData>,
&CompileGLTest::twoDimensions<Trade::MeshData2D>},
Containers::arraySize(Data2D));
addInstancedTests<CompileGLTest>({
&CompileGLTest::threeDimensions<Trade::MeshData>,
&CompileGLTest::threeDimensions<Trade::MeshData3D>},
Containers::arraySize(Data3D));
addInstancedTests({&CompileGLTest::threeDimensions},
Containers::arraySize(Data3D));
addTests({&CompileGLTest::unknownAttribute,
&CompileGLTest::generateNormalsNoPosition,
&CompileGLTest::generateNormals2DPosition});
addInstancedTests({&CompileGLTest::externalBuffers},
Containers::arraySize(DataExternal));
addTests({&CompileGLTest::externalBuffersInvalid});
/* Load the plugins directly from the build tree. Otherwise they're either
static and already loaded or not present in the build tree */
@ -187,7 +220,19 @@ CompileGLTest::CompileGLTest() {
.setSubImage(0, {}, ImageView2D{PixelFormat::RGBA8Unorm, {4, 4}, ImageData});
}
void CompileGLTest::twoDimensions() {
template<class T> struct MeshTypeName;
template<> struct MeshTypeName<Trade::MeshData> {
static const char* name() { return "Trade::MeshData"; }
};
template<> struct MeshTypeName<Trade::MeshData2D> {
static const char* name() { return "Trade::MeshData2D"; }
};
template<> struct MeshTypeName<Trade::MeshData3D> {
static const char* name() { return "Trade::MeshData3D"; }
};
template<class T> void CompileGLTest::twoDimensions() {
setTestCaseTemplateName(MeshTypeName<T>::name());
auto&& data = Data2D[testCaseInstanceId()];
setTestCaseDescription(data.name);
@ -202,69 +247,57 @@ void CompileGLTest::twoDimensions() {
|/ |/ |
0-----1-----2
*/
std::vector<Vector2> positions{
{-0.75f, -0.75f},
{ 0.00f, -0.75f},
{ 0.75f, -0.75f},
{-0.75f, 0.0f},
{ 0.00f, 0.0f},
{ 0.75f, 0.0f},
{-0.75f, 0.75f},
{ 0.0f, 0.75f},
{ 0.75f, 0.75f}
const struct Vertex {
Vector2 position;
Vector2 textureCoordinates;
Color3 color;
} vertexData[]{
{{-0.75f, -0.75f}, {0.0f, 0.0f}, 0x00ff00_rgbf},
{{ 0.00f, -0.75f}, {0.5f, 0.0f}, 0x808000_rgbf},
{{ 0.75f, -0.75f}, {1.0f, 0.0f}, 0xff0000_rgbf},
{{-0.75f, 0.00f}, {0.0f, 0.5f}, 0x00ff80_rgbf},
{{ 0.00f, 0.00f}, {0.5f, 0.5f}, 0x808080_rgbf},
{{ 0.75f, 0.00f}, {1.0f, 0.5f}, 0xff0080_rgbf},
{{-0.75f, 0.75f}, {0.0f, 1.0f}, 0x00ffff_rgbf},
{{ 0.0f, 0.75f}, {0.5f, 1.0f}, 0x8080ff_rgbf},
{{ 0.75f, 0.75f}, {1.0f, 1.0f}, 0xff00ff_rgbf}
};
std::vector<std::vector<Vector2>> textureCoordinates2D;
if(data.flags & Flag::TextureCoordinates2D) textureCoordinates2D.push_back(std::vector<Vector2>{
{0.0f, 0.0f},
{0.5f, 0.0f},
{1.0f, 0.0f},
{0.0f, 0.5f},
{0.5f, 0.5f},
{1.0f, 0.5f},
{0.0f, 1.0f},
{0.5f, 1.0f},
{1.0f, 1.0f}
});
std::vector<std::vector<Color4>> colors;
if(data.flags & Flag::Colors) colors.push_back(std::vector<Color4> {
0x00ff00_rgbf,
0x808000_rgbf,
0xff0000_rgbf,
0x00ff80_rgbf,
0x808080_rgbf,
0xff0080_rgbf,
0x00ffff_rgbf,
0x8080ff_rgbf,
0xff00ff_rgbf
});
std::vector<UnsignedInt> indices{
Containers::Array<Trade::MeshAttributeData> attributeData;
arrayAppend(attributeData, Trade::MeshAttributeData{
Trade::MeshAttribute::Position,
Containers::stridedArrayView(vertexData, &vertexData[0].position,
Containers::arraySize(vertexData), sizeof(Vertex))});
if(data.flags & Flag::TextureCoordinates2D)
arrayAppend(attributeData, Trade::MeshAttributeData{
Trade::MeshAttribute::TextureCoordinates,
Containers::stridedArrayView(vertexData, &vertexData[0].textureCoordinates,
Containers::arraySize(vertexData), sizeof(Vertex))});
if(data.flags & Flag::Colors)
arrayAppend(attributeData, Trade::MeshAttributeData{
Trade::MeshAttribute::Color,
Containers::stridedArrayView(vertexData, &vertexData[0].color,
Containers::arraySize(vertexData), sizeof(Vertex))});
const UnsignedInt indexData[]{
0, 1, 4, 0, 4, 3,
1, 2, 5, 1, 5, 4,
3, 4, 7, 3, 7, 6,
4, 5, 8, 4, 8, 7
};
/* Duplicate positions if data are non-indexed. Testing only positions
alone ATM, don't bother with other attribs. */
if(data.flags & Flag::NonIndexed) {
CORRADE_INTERNAL_ASSERT(textureCoordinates2D.empty());
CORRADE_INTERNAL_ASSERT(colors.empty());
positions = duplicate(indices, positions);
indices.clear();
}
Trade::MeshData meshData{MeshPrimitive::Triangles,
{}, indexData, Trade::MeshIndexData{indexData},
{}, vertexData, std::move(attributeData)};
/* Duplicate everything if data is non-indexed */
if(data.flags & Flag::NonIndexed) meshData = duplicate(meshData);
MAGNUM_VERIFY_NO_GL_ERROR();
GL::Mesh mesh = compile(Trade::MeshData2D{MeshPrimitive::Triangles, indices, {positions}, textureCoordinates2D, colors});
GL::Mesh mesh = compile(T{std::move(meshData)});
MAGNUM_VERIFY_NO_GL_ERROR();
@ -312,7 +345,8 @@ void CompileGLTest::twoDimensions() {
}
}
void CompileGLTest::threeDimensions() {
template<class T> void CompileGLTest::threeDimensions() {
setTestCaseTemplateName(MeshTypeName<T>::name());
auto&& data = Data3D[testCaseInstanceId()];
setTestCaseDescription(data.name);
@ -327,96 +361,77 @@ void CompileGLTest::threeDimensions() {
|/ |/ |
0-----1-----2
*/
std::vector<Vector3> positions{
{-0.75f, -0.75f, -0.35f},
{ 0.00f, -0.75f, -0.25f},
{ 0.75f, -0.75f, -0.35f},
{-0.75f, 0.00f, -0.25f},
{ 0.00f, 0.00f, 0.00f},
{ 0.75f, 0.00f, -0.25f},
{-0.75f, 0.75f, -0.35f},
{ 0.0f, 0.75f, -0.25f},
{ 0.75f, 0.75f, -0.35f}
const struct Vertex {
Vector3 position;
Vector3 normal;
Vector2 textureCoordinates;
Color4 color;
} vertexData[]{
{{-0.75f, -0.75f, -0.35f}, Vector3{-0.5f, -0.5f, 1.0f}.normalized(),
{0.0f, 0.0f}, 0x00ff00_rgbf},
{{ 0.00f, -0.75f, -0.25f}, Vector3{ 0.0f, -0.5f, 1.0f}.normalized(),
{0.5f, 0.0f}, 0x808000_rgbf},
{{ 0.75f, -0.75f, -0.35f}, Vector3{ 0.5f, -0.5f, 1.0f}.normalized(),
{1.0f, 0.0f}, 0xff0000_rgbf},
{{-0.75f, 0.00f, -0.25f}, Vector3{-0.5f, 0.0f, 1.0f}.normalized(),
{0.0f, 0.5f}, 0x00ff80_rgbf},
{{ 0.00f, 0.00f, 0.00f}, Vector3{ 0.0f, 0.0f, 1.0f}.normalized(),
{0.5f, 0.5f}, 0x808080_rgbf},
{{ 0.75f, 0.00f, -0.25f}, Vector3{ 0.5f, 0.0f, 1.0f}.normalized(),
{1.0f, 0.5f}, 0xff0080_rgbf},
{{-0.75f, 0.75f, -0.35f}, Vector3{-0.5f, 0.5f, 1.0f}.normalized(),
{0.0f, 1.0f}, 0x00ffff_rgbf},
{{ 0.0f, 0.75f, -0.25f}, Vector3{ 0.0f, 0.5f, 1.0f}.normalized(),
{0.5f, 1.0f}, 0x8080ff_rgbf},
{{ 0.75f, 0.75f, -0.35f}, Vector3{ 0.5f, 0.5f, 1.0f}.normalized(),
{1.0f, 1.0f}, 0xff00ff_rgbf}
};
std::vector<std::vector<Vector3>> normals;
if(data.flags & Flag::Normals) normals.push_back(std::vector<Vector3>{
Vector3{-0.5f, -0.5f, 1.0f}.normalized(),
Vector3{ 0.0f, -0.5f, 1.0f}.normalized(),
Vector3{ 0.5f, -0.5f, 1.0f}.normalized(),
Vector3{-0.5f, 0.0f, 1.0f}.normalized(),
Vector3{ 0.0f, 0.0f, 1.0f}.normalized(),
Vector3{ 0.5f, 0.0f, 1.0f}.normalized(),
Vector3{-0.5f, 0.5f, 1.0f}.normalized(),
Vector3{ 0.0f, 0.5f, 1.0f}.normalized(),
Vector3{ 0.5f, 0.5f, 1.0f}.normalized(),
});
std::vector<std::vector<Vector2>> textureCoordinates2D;
if(data.flags & Flag::TextureCoordinates2D) textureCoordinates2D.push_back(std::vector<Vector2>{
{0.0f, 0.0f},
{0.5f, 0.0f},
{1.0f, 0.0f},
{0.0f, 0.5f},
{0.5f, 0.5f},
{1.0f, 0.5f},
{0.0f, 1.0f},
{0.5f, 1.0f},
{1.0f, 1.0f}
});
std::vector<std::vector<Color4>> colors;
if(data.flags & Flag::Colors) colors.push_back(std::vector<Color4> {
0x00ff00_rgbf,
0x808000_rgbf,
0xff0000_rgbf,
0x00ff80_rgbf,
0x808080_rgbf,
0xff0080_rgbf,
0x00ffff_rgbf,
0x8080ff_rgbf,
0xff00ff_rgbf
});
std::vector<UnsignedInt> indices{
Containers::Array<Trade::MeshAttributeData> attributeData;
arrayAppend(attributeData, Trade::MeshAttributeData{
Trade::MeshAttribute::Position,
Containers::stridedArrayView(vertexData, &vertexData[0].position,
Containers::arraySize(vertexData), sizeof(Vertex))});
if(data.flags & Flag::Normals)
arrayAppend(attributeData, Trade::MeshAttributeData{
Trade::MeshAttribute::Normal,
Containers::stridedArrayView(vertexData, &vertexData[0].normal,
Containers::arraySize(vertexData), sizeof(Vertex))});
if(data.flags & Flag::TextureCoordinates2D)
arrayAppend(attributeData, Trade::MeshAttributeData{
Trade::MeshAttribute::TextureCoordinates,
Containers::stridedArrayView(vertexData, &vertexData[0].textureCoordinates,
Containers::arraySize(vertexData), sizeof(Vertex))});
if(data.flags & Flag::Colors)
arrayAppend(attributeData, Trade::MeshAttributeData{
Trade::MeshAttribute::Color,
Containers::stridedArrayView(vertexData, &vertexData[0].color,
Containers::arraySize(vertexData), sizeof(Vertex))});
const UnsignedByte indexData[]{
0, 1, 4, 0, 4, 3,
1, 2, 5, 1, 5, 4,
3, 4, 7, 3, 7, 6,
4, 5, 8, 4, 8, 7
};
/* Duplicate everything if data are non-indexed */
if(data.flags & Flag::NonIndexed) {
positions = duplicate(indices, positions);
if(data.flags & Flag::Normals)
normals[0] = duplicate(indices, normals[0]);
if(data.flags & Flag::TextureCoordinates2D)
textureCoordinates2D[0] = duplicate(indices, textureCoordinates2D[0]);
Trade::MeshData meshData{MeshPrimitive::Triangles,
{}, indexData, Trade::MeshIndexData{indexData},
{}, vertexData, std::move(attributeData)};
if(data.flags & Flag::Colors)
colors[0] = duplicate(indices, colors[0]);
indices.clear();
}
/* Duplicate everything if data is non-indexed */
if(data.flags & Flag::NonIndexed) meshData = duplicate(meshData);
MAGNUM_VERIFY_NO_GL_ERROR();
CompileFlags flags;
if(data.flags & Flag::GeneratedFlatNormals)
flags |= CompileFlag::GenerateFlatNormals;
else if(data.flags & Flag::GeneratedSmoothNormals)
if(data.flags & Flag::GeneratedSmoothNormals)
flags |= CompileFlag::GenerateSmoothNormals;
GL::Mesh mesh = compile(Trade::MeshData3D{MeshPrimitive::Triangles, indices, {positions}, normals, textureCoordinates2D, colors}, flags);
GL::Mesh mesh = compile(T{std::move(meshData)}, flags);
MAGNUM_VERIFY_NO_GL_ERROR();
@ -527,6 +542,137 @@ void CompileGLTest::threeDimensions() {
}
}
void CompileGLTest::unknownAttribute() {
Trade::MeshData data{MeshPrimitive::Triangles,
nullptr, {Trade::MeshAttributeData{Trade::meshAttributeCustom(115),
VertexFormat::Short, nullptr}}};
std::ostringstream out;
Warning redirectError{&out};
MeshTools::compile(data);
CORRADE_COMPARE(out.str(),
"MeshTools::compile(): ignoring unknown attribute Trade::MeshAttribute::Custom(115)\n");
}
void CompileGLTest::generateNormalsNoPosition() {
Trade::MeshData data{MeshPrimitive::Triangles, 1};
std::ostringstream out;
Error redirectError{&out};
MeshTools::compile(data, CompileFlag::GenerateFlatNormals);
CORRADE_COMPARE(out.str(),
"MeshTools::compile(): the mesh has no positions, can't generate normals\n");
}
void CompileGLTest::generateNormals2DPosition() {
Trade::MeshData data{MeshPrimitive::Triangles,
nullptr, {Trade::MeshAttributeData{Trade::MeshAttribute::Position,
VertexFormat::Vector2, nullptr}}};
std::ostringstream out;
Error redirectError{&out};
MeshTools::compile(data, CompileFlag::GenerateFlatNormals);
CORRADE_COMPARE(out.str(),
"MeshTools::compile(): can't generate normals for VertexFormat::Vector2 positions\n");
}
void CompileGLTest::externalBuffers() {
auto&& data = DataExternal[testCaseInstanceId()];
setTestCaseDescription(data.name);
/*
6-----7-----8
| /| /|
| / | / |
|/ |/ |
3-----4-----5
| /| /|
| / | / |
|/ |/ |
0-----1-----2
*/
Vector2 positions[] {
{-0.75f, -0.75f},
{ 0.00f, -0.75f},
{ 0.75f, -0.75f},
{-0.75f, 0.00f},
{ 0.00f, 0.00f},
{ 0.75f, 0.00f},
{-0.75f, 0.75f},
{ 0.0f, 0.75f},
{ 0.75f, 0.75f}
};
const UnsignedShort indexData[]{
0, 1, 4, 0, 4, 3,
1, 2, 5, 1, 5, 4,
3, 4, 7, 3, 7, 6,
4, 5, 8, 4, 8, 7
};
Trade::MeshData meshData{MeshPrimitive::Triangles,
{}, indexData, Trade::MeshIndexData{indexData},
{}, positions, {Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::arrayView(positions)}}};
/* Duplicate everything if data is non-indexed */
if(!data.indexed) meshData = duplicate(meshData);
GL::Buffer indices{NoCreate};
if(meshData.isIndexed()) {
indices = GL::Buffer{GL::Buffer::TargetHint::ElementArray};
indices.setData(meshData.indexData());
}
GL::Buffer vertices{GL::Buffer::TargetHint::Array};
vertices.setData(meshData.vertexData());
MAGNUM_VERIFY_NO_GL_ERROR();
GL::Mesh mesh{NoCreate};
if(data.moveIndices && data.moveVertices)
mesh = compile(meshData, std::move(indices), std::move(vertices));
else if(data.moveIndices && !data.moveVertices)
mesh = compile(meshData, std::move(indices), vertices);
else if(!data.moveIndices && data.moveVertices)
mesh = compile(meshData, indices, std::move(vertices));
else
mesh = compile(meshData, indices, vertices);
MAGNUM_VERIFY_NO_GL_ERROR();
if(!(_manager.loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
!(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded))
CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found.");
_framebuffer.clear(GL::FramebufferClear::Color);
_flat2D.setColor(0xff3366_rgbf);
mesh.draw(_flat2D);
MAGNUM_VERIFY_NO_GL_ERROR();
CORRADE_COMPARE_WITH(
_framebuffer.read({{}, {32, 32}}, {PixelFormat::RGBA8Unorm}),
Utility::Directory::join(COMPILEGLTEST_TEST_DIR, "flat2D.tga"),
(DebugTools::CompareImageToFile{_manager}));
}
void CompileGLTest::externalBuffersInvalid() {
Trade::MeshData data{MeshPrimitive::Triangles, 5};
Trade::MeshData indexedData{MeshPrimitive::Triangles,
nullptr, Trade::MeshIndexData{MeshIndexType::UnsignedInt, nullptr},
{}};
std::ostringstream out;
Error redirectError{&out};
compile(data, GL::Buffer{NoCreate}, GL::Buffer{}); /* this is okay */
compile(data, GL::Buffer{NoCreate}, GL::Buffer{NoCreate});
compile(indexedData, GL::Buffer{NoCreate}, GL::Buffer{});
CORRADE_COMPARE(out.str(),
"MeshTools::compile(): invalid external buffer(s)\n"
"MeshTools::compile(): invalid external buffer(s)\n");
}
}}}}
CORRADE_TEST_MAIN(Magnum::MeshTools::Test::CompileGLTest)

Loading…
Cancel
Save