Browse Source

MeshTools: initial utility for compiling a mesh for the line shader.

Currently just the bare minimum, more features such as handling
multiple contiguous strips and loops inside a single mesh or an
overlapping layout will come later.
pull/601/head
Vladimír Vondruš 4 years ago
parent
commit
e3c7410ac7
  1. 9
      doc/snippets/MagnumShaders-gl.cpp
  2. 8
      src/Magnum/MeshTools/CMakeLists.txt
  3. 84
      src/Magnum/MeshTools/CompileLines.cpp
  4. 92
      src/Magnum/MeshTools/CompileLines.h
  5. 287
      src/Magnum/MeshTools/Implementation/GenerateLines.h
  6. 37
      src/Magnum/MeshTools/Test/CMakeLists.txt
  7. 368
      src/Magnum/MeshTools/Test/CompileLinesGLTest.cpp
  8. 627
      src/Magnum/MeshTools/Test/CompileLinesTest.cpp
  9. BIN
      src/Magnum/MeshTools/Test/CompileLinesTestFiles/bevel.tga
  10. BIN
      src/Magnum/MeshTools/Test/CompileLinesTestFiles/line-primitive.tga
  11. BIN
      src/Magnum/MeshTools/Test/CompileLinesTestFiles/miter.tga
  12. BIN
      src/Magnum/MeshTools/Test/CompileLinesTestFiles/vertex-color.tga
  13. 6
      src/Magnum/Shaders/LineGL.h
  14. 2
      src/Magnum/Shaders/Test/LineGLTest.cpp

9
doc/snippets/MagnumShaders-gl.cpp

@ -64,6 +64,8 @@
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
#include "Magnum/GL/TextureArray.h" #include "Magnum/GL/TextureArray.h"
#include "Magnum/Primitives/Circle.h"
#include "Magnum/MeshTools/CompileLines.h"
#include "Magnum/Shaders/DistanceFieldVector.h" #include "Magnum/Shaders/DistanceFieldVector.h"
#include "Magnum/Shaders/Flat.h" #include "Magnum/Shaders/Flat.h"
#include "Magnum/Shaders/Generic.h" #include "Magnum/Shaders/Generic.h"
@ -698,6 +700,13 @@ vert.addSource(Utility::format(
} }
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
{
/* [LineGL-usage] */
Trade::MeshData circle = Primitives::circle2DWireframe(16);
GL::Mesh mesh = MeshTools::compileLines(circle);
/* [LineGL-usage] */
}
{ {
GL::Mesh mesh; GL::Mesh mesh;
Matrix3 transformationMatrix, projectionMatrix; Matrix3 transformationMatrix, projectionMatrix;

8
src/Magnum/MeshTools/CMakeLists.txt

@ -88,6 +88,14 @@ if(MAGNUM_TARGET_GL)
list(APPEND MagnumMeshTools_HEADERS list(APPEND MagnumMeshTools_HEADERS
Compile.h Compile.h
FullScreenTriangle.h) FullScreenTriangle.h)
if(NOT MAGNUM_TARGET_GLES2)
list(APPEND MagnumMeshTools_GracefulAssert_SRCS
CompileLines.cpp)
list(APPEND MagnumMeshTools_HEADERS
CompileLines.h)
endif()
endif() endif()
# Objects shared between main and test library # Objects shared between main and test library

84
src/Magnum/MeshTools/CompileLines.cpp

@ -0,0 +1,84 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021, 2022 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 "CompileLines.h"
#include "Magnum/GL/Buffer.h"
#include "Magnum/GL/Mesh.h"
#include "Magnum/MeshTools/Compile.h"
#include "Magnum/MeshTools/Implementation/GenerateLines.h"
/* This header is included only privately and doesn't introduce any linker
dependency (taking just the PreviousPosition, NextPosition and Annotation
attribute typedefs), thus it's completely safe to not link to the Shaders
library */
#include "Magnum/Shaders/LineGL.h"
namespace Magnum { namespace MeshTools {
GL::Mesh compileLines(const Trade::MeshData& lineMesh) {
Trade::MeshData mesh = Implementation::generateLines(lineMesh);
#ifdef CORRADE_GRACEFUL_ASSERT
/* If it asserted inside, bail */
if(!mesh.attributeCount()) return GL::Mesh{};
#endif
/* Upload the buffers, bind the line-specific attributes manually */
GL::Buffer indices{GL::Buffer::TargetHint::ElementArray, mesh.indexData()};
GL::Buffer vertices{GL::Buffer::TargetHint::Array, mesh.vertexData()};
GL::Mesh out = compile(mesh, std::move(indices), vertices);
/* Warn about attributes that are conflicting with line-specific attributes
and thus will get overwritten */
static_assert(Shaders::GenericGL3D::TextureCoordinates::Location == UnsignedInt(Shaders::LineGL3D::Annotation::Location), "");
static_assert(Shaders::GenericGL3D::Tangent::Location == UnsignedInt(Shaders::LineGL3D::PreviousPosition::Location), "");
static_assert(Shaders::GenericGL3D::Normal::Location == UnsignedInt(Shaders::LineGL3D::NextPosition::Location), "");
if(const Containers::Optional<UnsignedInt> id = lineMesh.findAttributeId(Trade::MeshAttribute::TextureCoordinates))
Warning{} << "MeshTools::compileLines():" << lineMesh.attributeName(*id) << "conflicts with line annotation attribute, ignoring";
if(const Containers::Optional<UnsignedInt> id = lineMesh.findAttributeId(Trade::MeshAttribute::Tangent))
Warning{} << "MeshTools::compileLines():" << lineMesh.attributeName(*id) << "conflicts with line previous position attribute, ignoring";
if(const Containers::Optional<UnsignedInt> id = lineMesh.findAttributeId(Trade::MeshAttribute::Normal))
Warning{} << "MeshTools::compileLines():" << lineMesh.attributeName(*id) << "conflicts with line next position attribute, ignoring";
/* PreviousPosition / NextPosition are bound to the same location in both
2D and 3D, using the 3D variant so it can be trimmed to just two
components in 2D (which wouldn't be possible the other way around) */
out.addVertexBuffer(vertices,
mesh.attributeOffset(Implementation::MeshAttributePreviousPosition),
mesh.attributeStride(Implementation::MeshAttributePreviousPosition),
GL::DynamicAttribute{Shaders::LineGL3D::PreviousPosition{}, mesh.attributeFormat(Implementation::MeshAttributePreviousPosition)});
out.addVertexBuffer(vertices,
mesh.attributeOffset(Implementation::MeshAttributeNextPosition),
mesh.attributeStride(Implementation::MeshAttributeNextPosition),
GL::DynamicAttribute{Shaders::LineGL3D::NextPosition{}, mesh.attributeFormat(Implementation::MeshAttributeNextPosition)});
out.addVertexBuffer(std::move(vertices),
mesh.attributeOffset(Implementation::MeshAttributeAnnotation),
mesh.attributeStride(Implementation::MeshAttributeAnnotation),
GL::DynamicAttribute{Shaders::LineGL3D::Annotation{}, mesh.attributeFormat(Implementation::MeshAttributeAnnotation)});
return out;
}
}}

92
src/Magnum/MeshTools/CompileLines.h

@ -0,0 +1,92 @@
#ifndef Magnum_MeshTools_CompileLines_h
#define Magnum_MeshTools_CompileLines_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021, 2022 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.
*/
#if defined(MAGNUM_TARGET_GL) && !defined(MAGNUM_TARGET_GLES2)
/** @file
* @brief Function @ref Magnum::MeshTools::compileLines()
* @m_since_latest
*/
#endif
#include "Magnum/configure.h"
#if defined(MAGNUM_TARGET_GL) && !defined(MAGNUM_TARGET_GLES2)
#include "Magnum/GL/GL.h"
#include "Magnum/MeshTools/visibility.h"
#include "Magnum/Trade/Trade.h"
namespace Magnum { namespace MeshTools {
/**
@brief Compile a line mesh for use with @ref Shaders::LineGL
@m_since_latest
Returns a @ref MeshPrimitive::Triangles mesh with
@ref MeshIndexType::UnsignedInt indices, all input attributes preserved in
their original format, and additionally with
@ref Shaders::LineGL::PreviousPosition and @ref Shaders::LineGL::NextPosition
attributes added in the same format as the input
@ref Trade::MeshAttribute::Position, and the @ref Shaders::LineGL::Annotation
attribute as @ref VertexFormat::UnsignedInt, according to the
@ref Shaders-LineGL-mesh-representation documentation of the shader.
Each line segment in the input vertices is converted to a quad, with first two
vertices inheriting vertex data from the first point of the segment and second
two vertices inheriting data from the second point of the segment. If the input
mesh is indexed, it's deindexed first. Neighbor information from a
@ref MeshPrimitive::LineStrip or @ref MeshPrimitive::LineLoop mesh is used to
form a single contiguous strip or a loop, @ref MeshPrimitive::Lines is treated
as loose segments.
For compatibility with shaders other than @ref Shaders::LineGL, the output mesh
can be also interpreted as indexed @ref MeshPrimitive::Lines --- out of every
six indices forming a quad, two will form a line segment between the two
original points, and the remaining four collapse into two degenerate line
segments.
Expects that the mesh contains at least a @ref Trade::MeshAttribute::Position
and is a line @relativeref{Magnum,MeshPrimitive}.
@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.
@requires_gles30 @ref Shaders::LineGL requires integer support in shaders which
is not available in OpenGL ES 2.0, thus neither this function is defined
in OpenGL ES 2.0 builds.
@requires_webgl20 @ref Shaders::LineGL requires integer support in shaders which
is not available in WebGL 1.0, thus neither this function is defined in
WebGL 1.0 builds.
*/
MAGNUM_MESHTOOLS_EXPORT GL::Mesh compileLines(const Trade::MeshData& lineMesh);
}}
#else
#error this header is available only in the desktop OpenGL, OpenGL ES 3.0+ and WebGL 2.0 builds
#endif
#endif

287
src/Magnum/MeshTools/Implementation/GenerateLines.h

@ -0,0 +1,287 @@
#ifndef Magnum_MeshTools_Implementation_GenerateLines_h
#define Magnum_MeshTools_Implementation_GenerateLines_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021, 2022 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 <Corrade/Containers/GrowableArray.h>
#include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/StridedArrayView.h>
#include <Corrade/Utility/Algorithms.h>
#include "Magnum/MeshTools/Duplicate.h"
#include "Magnum/MeshTools/GenerateIndices.h"
#include "Magnum/Trade/MeshData.h"
/* This header is included only privately and doesn't introduce any linker
dependency (taking just the LineVertexAnnotations enum), thus it's
completely safe to not link to the Shaders library */
#include "Magnum/Shaders/Line.h"
namespace Magnum { namespace MeshTools { namespace Implementation { namespace {
/* This code is used internally by compileLines() and returns a MeshData
instance in order to be testable. The intention is to eventually make this
API public and the custom attributes builtin in order to make it possible to
(for example) produce glTF files that are already directly renderable as
wide line meshes. */
constexpr Trade::MeshAttribute MeshAttributePreviousPosition = Trade::meshAttributeCustom(32765);
constexpr Trade::MeshAttribute MeshAttributeNextPosition = Trade::meshAttributeCustom(32766);
constexpr Trade::MeshAttribute MeshAttributeAnnotation = Trade::meshAttributeCustom(32767);
Trade::MeshData generateLines(const Trade::MeshData& lineMesh) {
CORRADE_ASSERT(lineMesh.primitive() == MeshPrimitive::Lines ||
lineMesh.primitive() == MeshPrimitive::LineStrip ||
lineMesh.primitive() == MeshPrimitive::LineLoop,
"Trade::MeshTools::compileLines(): expected a line primitive, got" << lineMesh.primitive(), (Trade::MeshData{MeshPrimitive::Triangles, 0}));
/** @todo this will assert if the count in MeshData is wrong, check here
already */
const UnsignedInt quadCount = primitiveCount(lineMesh.primitive(), lineMesh.isIndexed() ? lineMesh.indexCount() : lineMesh.vertexCount());
/** @todo combine this allocation with pointDuplicationIndices below, and
then reuse for the final index buffer */
Containers::Array<UnsignedInt> originalIndices;
if(lineMesh.primitive() == MeshPrimitive::Lines) {
if(lineMesh.isIndexed())
originalIndices = lineMesh.indicesAsArray();
} else {
if(lineMesh.primitive() == MeshPrimitive::LineStrip) {
if(lineMesh.isIndexed())
originalIndices = generateLineStripIndices(lineMesh.indices());
else
originalIndices = generateLineStripIndices(lineMesh.vertexCount());
} else if(lineMesh.primitive() == MeshPrimitive::LineLoop) {
if(lineMesh.isIndexed())
originalIndices = generateLineLoopIndices(lineMesh.indices());
else
originalIndices = generateLineLoopIndices(lineMesh.vertexCount());
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
/* Create a source index array for duplicate() by combining indices of a
form 00112233 (i.e., duplicating every point twice) with the original
mesh indices (if there are any) */
Containers::Array<UnsignedInt> pointDuplicationIndices{NoInit, quadCount*4};
for(UnsignedInt i = 0; i != quadCount; ++i) {
pointDuplicationIndices[i*4 + 0] =
pointDuplicationIndices[i*4 + 1] = i*2 + 0;
pointDuplicationIndices[i*4 + 2] =
pointDuplicationIndices[i*4 + 3] = i*2 + 1;
}
if(originalIndices)
duplicateInto(pointDuplicationIndices, Containers::arrayCast<2, char>(stridedArrayView(originalIndices)), Containers::arrayCast<2, char>(stridedArrayView(pointDuplicationIndices)));
/* Position is required, everything else is optional */
const Containers::Optional<UnsignedInt> positionAttributeId = lineMesh.findAttributeId(Trade::MeshAttribute::Position);
CORRADE_ASSERT(positionAttributeId,
"Trade::MeshTools::compileLines(): the mesh has no positions", (Trade::MeshData{MeshPrimitive::Triangles, 0}));
/* Duplicate the input mesh to have each input line segment turned into
four vertices for a quad. Allocate space for the additional attributes
as well. */
Trade::MeshData mesh = duplicate(Trade::MeshData{MeshPrimitive::Triangles, {}, pointDuplicationIndices, Trade::MeshIndexData{pointDuplicationIndices}, {}, lineMesh.vertexData(), Trade::meshAttributeDataNonOwningArray(lineMesh.attributeData()), lineMesh.vertexCount()}, {
Trade::MeshAttributeData{MeshAttributePreviousPosition,
lineMesh.attributeFormat(*positionAttributeId), nullptr},
Trade::MeshAttributeData{MeshAttributeNextPosition,
lineMesh.attributeFormat(*positionAttributeId), nullptr},
/** @todo use a 8-bit type and make the attribute non-interleaved to
save space? */
Trade::MeshAttributeData{MeshAttributeAnnotation,
VertexFormat::UnsignedInt, nullptr},
});
CORRADE_INTERNAL_ASSERT(mesh.attributeName(*positionAttributeId) == Trade::MeshAttribute::Position);
/* Fill in previous/next positions, if we have any vertices at all */
if(quadCount) {
/* Form 3D arrays where the second dimension is 2 elements */
const Containers::StridedArrayView2D<const char> positions = mesh.attribute(Trade::MeshAttribute::Position);
const Containers::StridedArrayView3D<const char> positions3{mesh.vertexData(), static_cast<const char*>(positions.data()),
{positions.size()[0]/2, 2, positions.size()[1]},
{positions.stride()[0]*2, positions.stride()[0], positions.stride()[1]}};
const Containers::StridedArrayView2D<char> previousPositions = mesh.mutableAttribute(MeshAttributePreviousPosition);
const Containers::StridedArrayView3D<char> previousPositions3{mesh.mutableVertexData(), static_cast<char*>(previousPositions.data()),
{previousPositions.size()[0]/2, 2, previousPositions.size()[1]},
{previousPositions.stride()[0]*2, previousPositions.stride()[0], previousPositions.stride()[1]}};
const Containers::StridedArrayView2D<char> nextPositions = mesh.mutableAttribute(MeshAttributeNextPosition);
const Containers::StridedArrayView3D<char> nextPositions3{mesh.mutableVertexData(), static_cast<char*>(nextPositions.data()),
{nextPositions.size()[0]/2, 2, nextPositions.size()[1]},
{nextPositions.stride()[0]*2, nextPositions.stride()[0], nextPositions.stride()[1]}};
/* Zero-init all previous/next positions for predictable output */
/** @todo have NoInit / ValueInit overload of duplicate(), interleave()
and interleavedLayout() instea, significantly faster than doing it
manually on sparse views after */
{
constexpr const char Zero[sizeof(Float)*3]{};
Containers::StridedArrayView2D<const char> zeros{Zero, positions.size(), {0, 1}};
Utility::copy(zeros, previousPositions);
Utility::copy(zeros, nextPositions);
}
/* Given AABBCCDDEEFF, we want to copy Position from AA__CC__EE__ to
__BB__DD__FF's PreviousPosition, and Position from __BB__DD__FF to
AA__CC__EE__'s NextPosition. Strip one group of 2 from either prefix
or suffix, pick every 2nd in the first dimension, and copy. */
Utility::copy(
positions3.exceptSuffix(1).every(2),
previousPositions3.exceptPrefix(1).every(2));
Utility::copy(
positions3.exceptPrefix(1).every(2),
nextPositions3.exceptSuffix(1).every(2));
/* Fill in previous/next neighbor positions if this is a line loop /
line strip and there's more than one quad. Given AABBCCDDEEFF, want
to copy Position from AA__CC______ to ____CC__EE__'s
PreviousPosition, and Position from ______DD__FF to __BB__DD____'s
NextPosition, and in case of loops also Position from ________EE__
to AA__________'s PreviousPosition and Position from __BB________ to
__________FF's NextPosition. */
/** @todo put together with the annotations once it's generalized to
the index buffer */
if((lineMesh.primitive() == MeshPrimitive::LineStrip ||
lineMesh.primitive() == MeshPrimitive::LineLoop) && quadCount > 1) {
Utility::copy(
positions3.exceptSuffix(2).every(2),
previousPositions3.exceptPrefix(2).every(2));
Utility::copy(
positions3.exceptPrefix(3).every(2),
nextPositions3.exceptPrefix(1).exceptSuffix(2).every(2));
}
if(lineMesh.primitive() == MeshPrimitive::LineLoop) {
Utility::copy(positions3[positions3.size()[0] - 2], previousPositions3.front());
Utility::copy(positions3[1], nextPositions3.back());
}
}
/* Fill in point annotation */
const Containers::StridedArrayView1D<Shaders::LineVertexAnnotations> annotations = Containers::arrayCast<Shaders::LineVertexAnnotations>(mesh.mutableAttribute<UnsignedInt>(MeshAttributeAnnotation));
for(UnsignedInt i = 0; i != quadCount; ++i) {
annotations[i*4 + 0] = Shaders::LineVertexAnnotation::Up|Shaders::LineVertexAnnotation::Begin;
annotations[i*4 + 1] = Shaders::LineVertexAnnotation::Begin;
annotations[i*4 + 2] = Shaders::LineVertexAnnotation::Up;
annotations[i*4 + 3] = {};
}
/* A line strip has joins everywhere except the first and last two
vertices; line loop joins also the first and last two vertices if it's
non-empty */
/** @todo add a flag to use the original index buffer somehow to figure out
abitrary joins and loops */
if(lineMesh.primitive() == MeshPrimitive::LineStrip ||
lineMesh.primitive() == MeshPrimitive::LineLoop) {
for(UnsignedInt i = 0; i != quadCount; ++i) {
annotations[i*4 + 0] |= Shaders::LineVertexAnnotation::Join;
annotations[i*4 + 1] |= Shaders::LineVertexAnnotation::Join;
annotations[i*4 + 2] |= Shaders::LineVertexAnnotation::Join;
annotations[i*4 + 3] |= Shaders::LineVertexAnnotation::Join;
}
}
if(quadCount && lineMesh.primitive() == MeshPrimitive::LineStrip) {
annotations[0] &= ~Shaders::LineVertexAnnotation::Join;
annotations[1] &= ~Shaders::LineVertexAnnotation::Join;
annotations[quadCount*4 - 2] &= ~Shaders::LineVertexAnnotation::Join;
annotations[quadCount*4 - 1] &= ~Shaders::LineVertexAnnotation::Join;
}
/* Create an index buffer */
Containers::Array<UnsignedInt> indexData;
arrayReserve(indexData, quadCount*6);
for(UnsignedInt i = 0; i != quadCount; ++i) {
/* The order is chosen in a way that makes it possible to interpret
the 6 indices as 3 lines instead of 2 triangles, and additionally
those forming only one line, with the other two degenerating to an
invisible point to avoid overlaps that would break blending.
0---2 2
| / /| 0---2
| / / |
|/ / | 11 32
1 1---3 */
arrayAppend(indexData, {
i*4 + 2,
i*4 + 0,
i*4 + 1,
i*4 + 1,
i*4 + 3,
i*4 + 2
});
/* Add also indices for the bevel in both orientations (one will always
degenerate). For the line fallback these will all degenerate.
2 2 2---4 4 4--
/| | / /| | 23 44
/ | | / / | | /
| |/ / | |/ 35
--3 3 3---5 5 5 */
if(i + 1 != quadCount && annotations[i*4 + 3] & Shaders::LineVertexAnnotation::Join) {
arrayAppend(indexData, {
i*4 + 2,
i*4 + 3,
i*4 + 4,
i*4 + 4,
i*4 + 3,
i*4 + 5,
});
}
}
/* And finally also bevel indices between the last and first segment in
case of loops, if the loop isn't empty
-2 -2---0 0 0-
/| | / /| |
| | / / | |
| |/ / | |/
-1 -1 -1--1 1 */
if(quadCount && annotations[0] & Shaders::LineVertexAnnotation::Join) {
CORRADE_INTERNAL_ASSERT(annotations[quadCount*4 - 1] & Shaders::LineVertexAnnotation::Join);
arrayAppend(indexData, {
quadCount*4 - 2,
quadCount*4 - 1,
0u,
0u,
quadCount*4 - 1,
1u
});
}
Trade::MeshIndexData indices{indexData};
return Trade::MeshData{mesh.primitive(),
indexData ? Containers::arrayAllocatorCast<char>(std::move(indexData)) : Containers::Array<char>{}, indices,
mesh.releaseVertexData(), mesh.releaseAttributeData()};
}
}}}}
#endif

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

@ -43,6 +43,12 @@ corrade_add_test(MeshToolsSubdivideTest SubdivideTest.cpp LIBRARIES Magnum Magnu
corrade_add_test(MeshToolsTipsifyTest TipsifyTest.cpp LIBRARIES MagnumMeshTools) corrade_add_test(MeshToolsTipsifyTest TipsifyTest.cpp LIBRARIES MagnumMeshTools)
corrade_add_test(MeshToolsTransformTest TransformTest.cpp LIBRARIES MagnumMeshToolsTestLib) corrade_add_test(MeshToolsTransformTest TransformTest.cpp LIBRARIES MagnumMeshToolsTestLib)
if(NOT MAGNUM_TARGET_GLES2)
corrade_add_test(MeshToolsCompileLinesTest CompileLinesTest.cpp
# Needs to link to Shaders for debug output for LineVertexAnnotations
LIBRARIES MagnumMeshToolsTestLib MagnumShaders)
endif()
# Graceful assert for testing # Graceful assert for testing
set_property(TARGET set_property(TARGET
MeshToolsConcatenateTest MeshToolsConcatenateTest
@ -125,4 +131,35 @@ if(MAGNUM_BUILD_GL_TESTS)
add_dependencies(MeshToolsCompileGLTest TgaImporter) add_dependencies(MeshToolsCompileGLTest TgaImporter)
endif() endif()
endif() endif()
if(NOT MAGNUM_TARGET_GLES2)
corrade_add_test(MeshToolsCompileLinesGLTest CompileLinesGLTest.cpp
LIBRARIES
MagnumDebugTools
MagnumMeshToolsTestLib
MagnumOpenGLTester
MagnumShaders
FILES
CompileLinesTestFiles/bevel.tga
CompileLinesTestFiles/line-primitive.tga
CompileLinesTestFiles/miter.tga
CompileLinesTestFiles/vertex-color.tga)
target_include_directories(MeshToolsCompileLinesGLTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>)
if(MAGNUM_BUILD_PLUGINS_STATIC)
if(MAGNUM_WITH_ANYIMAGEIMPORTER)
target_link_libraries(MeshToolsCompileLinesGLTest PRIVATE AnyImageImporter)
endif()
if(MAGNUM_WITH_TGAIMPORTER)
target_link_libraries(MeshToolsCompileLinesGLTest PRIVATE TgaImporter)
endif()
else()
# So the plugins get properly built when building the test
if(MAGNUM_WITH_ANYIMAGEIMPORTER)
add_dependencies(MeshToolsCompileLinesGLTest AnyImageImporter)
endif()
if(MAGNUM_WITH_TGAIMPORTER)
add_dependencies(MeshToolsCompileLinesGLTest TgaImporter)
endif()
endif()
endif()
endif() endif()

368
src/Magnum/MeshTools/Test/CompileLinesGLTest.cpp

@ -0,0 +1,368 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <sstream>
#include <Corrade/Containers/GrowableArray.h>
#include <Corrade/Containers/String.h>
#include <Corrade/Containers/StringIterable.h>
#include <Corrade/PluginManager/Manager.h>
#include <Corrade/Utility/DebugStl.h>
#include <Corrade/Utility/Path.h>
#include "Magnum/Image.h"
#include "Magnum/ImageView.h"
#include "Magnum/PixelFormat.h"
#include "Magnum/DebugTools/CompareImage.h"
#include "Magnum/GL/Framebuffer.h"
#include "Magnum/GL/Mesh.h"
#include "Magnum/GL/OpenGLTester.h"
#include "Magnum/GL/Renderbuffer.h"
#include "Magnum/GL/RenderbufferFormat.h"
#include "Magnum/Math/Color.h"
#include "Magnum/Math/Matrix3.h"
#include "Magnum/Math/Matrix4.h"
#include "Magnum/MeshTools/Compile.h"
#include "Magnum/MeshTools/CompileLines.h"
#include "Magnum/Shaders/FlatGL.h"
#include "Magnum/Shaders/Line.h"
#include "Magnum/Shaders/LineGL.h"
#include "Magnum/Trade/MeshData.h"
#include "Magnum/Trade/AbstractImporter.h"
#include "configure.h"
namespace Magnum { namespace MeshTools { namespace Test { namespace {
struct CompileLinesGLTest: GL::OpenGLTester {
explicit CompileLinesGLTest();
void renderSetup();
void renderTeardown();
void twoDimensions();
void threeDimensions();
void linePrimitiveCompatibility();
void conflictingAttributes();
void emptyMesh();
void notLines();
void noAttributes();
void noPositionAttribute();
PluginManager::Manager<Trade::AbstractImporter> _manager{"nonexistent"};
GL::Renderbuffer _color;
GL::Framebuffer _framebuffer{{{}, {32, 32}}};
};
using namespace Math::Literals;
const struct {
const char* name;
bool colors;
bool flip;
Shaders::LineJoinStyle joinStyle;
const char* expected;
} TwoDimensionsData[]{
{"", false, false, Shaders::LineJoinStyle::Miter, "miter.tga"},
{"bevel", false, false, Shaders::LineJoinStyle::Bevel, "bevel.tga"},
{"bevel, flipped", false, true, Shaders::LineJoinStyle::Bevel, "bevel.tga"},
{"vertex color", true, false, Shaders::LineJoinStyle::Miter, "vertex-color.tga"},
};
const struct {
const char* name;
Trade::MeshAttribute attribute;
VertexFormat format;
const char* expected;
} ConflictingAttributesData[]{
{"texture coordinates",
Trade::MeshAttribute::TextureCoordinates, VertexFormat::Vector2,
"MeshTools::compileLines(): Trade::MeshAttribute::TextureCoordinates conflicts with line annotation attribute, ignoring\n"},
{"tangent",
Trade::MeshAttribute::Tangent, VertexFormat::Vector3,
"MeshTools::compileLines(): Trade::MeshAttribute::Tangent conflicts with line previous position attribute, ignoring\n"},
{"normal",
Trade::MeshAttribute::Normal, VertexFormat::Vector3,
"MeshTools::compileLines(): Trade::MeshAttribute::Normal conflicts with line next position attribute, ignoring\n"},
};
CompileLinesGLTest::CompileLinesGLTest() {
addInstancedTests({&CompileLinesGLTest::twoDimensions},
Containers::arraySize(TwoDimensionsData),
&CompileLinesGLTest::renderSetup,
&CompileLinesGLTest::renderTeardown);
addTests({&CompileLinesGLTest::threeDimensions,
&CompileLinesGLTest::linePrimitiveCompatibility},
&CompileLinesGLTest::renderSetup,
&CompileLinesGLTest::renderTeardown);
addInstancedTests({&CompileLinesGLTest::conflictingAttributes},
Containers::arraySize(ConflictingAttributesData),
&CompileLinesGLTest::renderSetup,
&CompileLinesGLTest::renderTeardown);
addTests({&CompileLinesGLTest::emptyMesh,
&CompileLinesGLTest::notLines,
&CompileLinesGLTest::noAttributes,
&CompileLinesGLTest::noPositionAttribute});
/* Load the plugins directly from the build tree. Otherwise they're either
static and already loaded or not present in the build tree */
#ifdef ANYIMAGEIMPORTER_PLUGIN_FILENAME
CORRADE_INTERNAL_ASSERT_OUTPUT(_manager.load(ANYIMAGEIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded);
#endif
#ifdef TGAIMPORTER_PLUGIN_FILENAME
CORRADE_INTERNAL_ASSERT_OUTPUT(_manager.load(TGAIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded);
#endif
_color.setStorage(GL::RenderbufferFormat::RGBA8, {32, 32});
_framebuffer
.attachRenderbuffer(GL::Framebuffer::ColorAttachment{0}, _color)
.bind();
}
void CompileLinesGLTest::renderSetup() {
GL::Renderer::enable(GL::Renderer::Feature::FaceCulling);
_framebuffer.clear(GL::FramebufferClear::Color);
}
void CompileLinesGLTest::renderTeardown() {}
void CompileLinesGLTest::twoDimensions() {
auto&& data = TwoDimensionsData[testCaseInstanceId()];
setTestCaseDescription(data.name);
struct Vertex {
Vector2 position;
Color3 color;
} vertexData[]{
{{-1.0f, -1.0f}, 0xdcdcdc_rgbf},
{{+1.0f, -1.0f}, 0xdcdcdc_rgbf},
{{+1.0f, +1.0f}, 0x2f83cc_rgbf},
{{-1.0f, +1.0f}, 0x2f83cc_rgbf},
};
auto vertices = Containers::stridedArrayView(vertexData);
Containers::Array<Trade::MeshAttributeData> attributes;
arrayAppend(attributes, InPlaceInit, Trade::MeshAttribute::Position, vertices.slice(&Vertex::position));
if(data.colors)
arrayAppend(attributes, InPlaceInit, Trade::MeshAttribute::Color, vertices.slice(&Vertex::color));
GL::Mesh mesh = compileLines(Trade::MeshData{MeshPrimitive::LineLoop, {}, vertexData, std::move(attributes)});
Shaders::LineGL2D shader{Shaders::LineGL2D::Configuration{}
.setFlags(data.colors ? Shaders::LineGL2D::Flag::VertexColor : Shaders::LineGL2D::Flags{})
.setJoinStyle(data.joinStyle)};
shader
.setViewportSize({32, 32})
.setWidth(9)
.setTransformationProjectionMatrix(Matrix3::scaling(Vector2{21.0f/32.0f}*Vector2::yScale(data.flip ? -1.0f : 1.0f)))
.draw(mesh);
MAGNUM_VERIFY_NO_GL_ERROR();
CORRADE_COMPARE_WITH(
_framebuffer.read({{}, {32, 32}}, {PixelFormat::RGBA8Unorm}),
Utility::Path::join({MESHTOOLS_TEST_DIR, "CompileLinesTestFiles", data.expected}),
(DebugTools::CompareImageToFile{_manager}));
}
void CompileLinesGLTest::threeDimensions() {
/* Same as the initial case in twoDimensions(), just in 3D and with a
varying Z (which shouldn't have any effect as it's rendering in an
orthographic projection) */
Vector3 positions[]{
{-1.0f, -1.0f, +0.5f},
{+1.0f, -1.0f, +0.5f},
{+1.0f, +1.0f, -0.5f},
{-1.0f, +1.0f, -0.5f},
};
GL::Mesh mesh = compileLines(Trade::MeshData{MeshPrimitive::LineLoop, {}, positions, {
Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::stridedArrayView(positions)}
}});
Shaders::LineGL3D{}
.setViewportSize({32, 32})
.setWidth(9)
.setTransformationProjectionMatrix(Matrix4::scaling({Vector2{21.0f/32.0f}, 1.0f}))
.draw(mesh);
MAGNUM_VERIFY_NO_GL_ERROR();
CORRADE_COMPARE_WITH(
_framebuffer.read({{}, {32, 32}}, {PixelFormat::RGBA8Unorm}),
Utility::Path::join(MESHTOOLS_TEST_DIR, "CompileLinesTestFiles/miter.tga"),
(DebugTools::CompareImageToFile{_manager}));
}
void CompileLinesGLTest::linePrimitiveCompatibility() {
struct Vertex {
Vector2 position;
Color3 color;
} vertexData[]{
{{-1.0f, -1.0f}, 0xdcdcdc_rgbf},
{{+1.0f, -1.0f}, 0xdcdcdc_rgbf},
{{+1.0f, +1.0f}, 0x2f83cc_rgbf},
{{-1.0f, +1.0f}, 0x2f83cc_rgbf},
};
auto vertices = Containers::stridedArrayView(vertexData);
/* Enabling blending and a half-transparent color to catch accidental
overlaps where they shouldn't be */
GL::Renderer::enable(GL::Renderer::Feature::Blending);
GL::Renderer::setBlendFunction(
GL::Renderer::BlendFunction::One,
GL::Renderer::BlendFunction::OneMinusSourceAlpha);
Trade::MeshData lineMeshData{MeshPrimitive::LineLoop, {}, vertexData, {
Trade::MeshAttributeData{Trade::MeshAttribute::Position, vertices.slice(&Vertex::position)},
Trade::MeshAttributeData{Trade::MeshAttribute::Color, vertices.slice(&Vertex::color)},
}};
Shaders::FlatGL2D shader{Shaders::FlatGL2D::Configuration{}
.setFlags(Shaders::FlatGL2D::Flag::VertexColor)};
shader
.setTransformationProjectionMatrix(Matrix3::scaling(Vector2{21.0f/32.0f}))
.setColor(0x80808080_rgbaf);
/* Render the original */
shader.draw(compile(lineMeshData));
MAGNUM_VERIFY_NO_GL_ERROR();
CORRADE_COMPARE_WITH(
_framebuffer.read({{}, {32, 32}}, {PixelFormat::RGBA8Unorm}),
Utility::Path::join(MESHTOOLS_TEST_DIR, "CompileLinesTestFiles/line-primitive.tga"),
(DebugTools::CompareImageToFile{_manager}));
_framebuffer.clear(GL::FramebufferClear::Color);
/* Render the line mesh with the primitive set back to lines. The index
buffer layout should be compatible with it, and produce the same
result. */
shader.draw(compileLines(lineMeshData).setPrimitive(MeshPrimitive::Lines));
MAGNUM_VERIFY_NO_GL_ERROR();
CORRADE_COMPARE_WITH(
_framebuffer.read({{}, {32, 32}}, {PixelFormat::RGBA8Unorm}),
Utility::Path::join(MESHTOOLS_TEST_DIR, "CompileLinesTestFiles/line-primitive.tga"),
(DebugTools::CompareImageToFile{_manager}));
GL::Renderer::disable(GL::Renderer::Feature::Blending);
}
void CompileLinesGLTest::conflictingAttributes() {
auto&& data = ConflictingAttributesData[testCaseInstanceId()];
setTestCaseDescription(data.name);
/* Same as the initial case in twoDimensions(), should just warn but
produce correct result */
struct Vertex {
Vector3 position;
Vector3 extra;
} vertexData[]{
{{-1.0f, -1.0f, +0.5f}, {}},
{{+1.0f, -1.0f, +0.5f}, {}},
{{+1.0f, +1.0f, -0.5f}, {}},
{{-1.0f, +1.0f, -0.5f}, {}},
};
auto vertices = Containers::stridedArrayView(vertexData);
Trade::MeshData lineMesh{MeshPrimitive::LineLoop, {}, vertexData, {
Trade::MeshAttributeData{Trade::MeshAttribute::Position, vertices.slice(&Vertex::position)},
Trade::MeshAttributeData{data.attribute, data.format, vertices.slice(&Vertex::extra)},
}};
std::ostringstream out;
GL::Mesh mesh{NoCreate};
{
Warning redirectWarning{&out};
mesh = compileLines(lineMesh);
}
Shaders::LineGL3D{}
.setViewportSize({32, 32})
.setWidth(9)
.setTransformationProjectionMatrix(Matrix4::scaling({Vector2{21.0f/32.0f}, 1.0f}))
.draw(mesh);
MAGNUM_VERIFY_NO_GL_ERROR();
CORRADE_COMPARE_WITH(
_framebuffer.read({{}, {32, 32}}, {PixelFormat::RGBA8Unorm}),
Utility::Path::join(MESHTOOLS_TEST_DIR, "CompileLinesTestFiles/miter.tga"),
(DebugTools::CompareImageToFile{_manager}));
CORRADE_COMPARE(out.str(), data.expected);
}
void CompileLinesGLTest::emptyMesh() {
GL::Mesh mesh = compileLines(Trade::MeshData{MeshPrimitive::LineLoop, {}, nullptr, {
Trade::MeshAttributeData{Trade::MeshAttribute::Position, VertexFormat::Vector2, nullptr}
}});
CORRADE_COMPARE(mesh.primitive(), GL::MeshPrimitive::Triangles);
CORRADE_VERIFY(mesh.isIndexed());
CORRADE_COMPARE(mesh.count(), 0);
}
void CompileLinesGLTest::notLines() {
CORRADE_SKIP_IF_NO_ASSERT();
Vector3 positions[3]{};
std::ostringstream out;
Error redirectError{&out};
compileLines(Trade::MeshData{MeshPrimitive::TriangleFan, {}, positions, {
Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::stridedArrayView(positions)}
}});
CORRADE_COMPARE(out.str(), "Trade::MeshTools::compileLines(): expected a line primitive, got MeshPrimitive::TriangleFan\n");
}
void CompileLinesGLTest::noAttributes() {
CORRADE_SKIP_IF_NO_ASSERT();
std::ostringstream out;
Error redirectError{&out};
compileLines(Trade::MeshData{MeshPrimitive::Lines, 12});
CORRADE_COMPARE(out.str(), "Trade::MeshTools::compileLines(): the mesh has no positions\n");
}
void CompileLinesGLTest::noPositionAttribute() {
CORRADE_SKIP_IF_NO_ASSERT();
Vector3 colors[2]{};
std::ostringstream out;
Error redirectError{&out};
compileLines(Trade::MeshData{MeshPrimitive::Lines, {}, colors, {
Trade::MeshAttributeData{Trade::MeshAttribute::Color, Containers::stridedArrayView(colors)}
}});
CORRADE_COMPARE(out.str(), "Trade::MeshTools::compileLines(): the mesh has no positions\n");
}
}}}}
CORRADE_TEST_MAIN(Magnum::MeshTools::Test::CompileLinesGLTest)

627
src/Magnum/MeshTools/Test/CompileLinesTest.cpp

@ -0,0 +1,627 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <sstream>
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/TestSuite/Compare/Container.h>
#include "Magnum/Math/PackingBatch.h"
#include "Magnum/MeshTools/Implementation/GenerateLines.h"
namespace Magnum { namespace MeshTools { namespace Test { namespace {
struct CompileLinesTest: TestSuite::Tester {
explicit CompileLinesTest();
template<class T> void oneLoop();
void extraAttributes();
void zeroVertices();
void twoVerticesStrip();
void twoVerticesLoop();
/* Non-line primitives and absence of position attribute tested in
CompileLinesGLTest, to verify it's not accessed earlier than the
assertion */
};
using namespace Math::Literals;
const struct {
const char* name;
MeshPrimitive primitive;
Containers::Array<Vector2> positions;
Containers::Array<UnsignedInt> indices;
bool expectedJoins;
bool expectedJoinsFirstLast;
} OneLoopData[]{
{"loose segments", MeshPrimitive::Lines, {InPlaceInit, {
{-1.0f, -1.0f}, {+1.0f, -1.0f},
{+1.0f, -1.0f}, {+1.0f, +1.0f},
{+1.0f, +1.0f}, {-1.0f, +1.0f},
{-1.0f, +1.0f}, {-1.0f, -1.0f}
}}, nullptr, false, false},
{"loose indexed segments", MeshPrimitive::Lines, {InPlaceInit, {
{-1.0f, -1.0f},
{+1.0f, -1.0f},
{+1.0f, +1.0f},
{-1.0f, +1.0f},
}}, {InPlaceInit, {
0, 1, 1, 2, 2, 3, 3, 0
}}, false, false},
/** @todo indexed segments that get connected */
{"loop", MeshPrimitive::LineLoop, {InPlaceInit, {
{-1.0f, -1.0f},
{+1.0f, -1.0f},
{+1.0f, +1.0f},
{-1.0f, +1.0f},
}}, nullptr, true, true},
{"indexed loop", MeshPrimitive::LineLoop, {InPlaceInit, {
{-1.0f, -1.0f},
{-1.0f, +1.0f},
{+1.0f, -1.0f},
{+1.0f, +1.0f},
}}, {InPlaceInit, {
0, 2, 3, 1
}}, true, true},
{"strip", MeshPrimitive::LineStrip, {InPlaceInit, {
{-1.0f, -1.0f},
{+1.0f, -1.0f},
{+1.0f, +1.0f},
{-1.0f, +1.0f},
{-1.0f, -1.0f},
}}, nullptr, true, false},
{"indexed strip", MeshPrimitive::LineStrip, {InPlaceInit, {
{-1.0f, -1.0f},
{-1.0f, +1.0f},
{+1.0f, -1.0f},
{+1.0f, +1.0f},
}}, {InPlaceInit, {
0, 2, 3, 1, 0
}}, true, false},
/** @todo closed (indexed) strip, once arbitrary index buffer looping is supported */
};
CompileLinesTest::CompileLinesTest() {
addInstancedTests<CompileLinesTest>({
&CompileLinesTest::oneLoop<UnsignedInt>,
&CompileLinesTest::oneLoop<UnsignedShort>,
&CompileLinesTest::oneLoop<UnsignedByte>},
Containers::arraySize(OneLoopData));
addTests({&CompileLinesTest::extraAttributes,
&CompileLinesTest::zeroVertices,
&CompileLinesTest::twoVerticesStrip,
&CompileLinesTest::twoVerticesLoop});
}
template<class T> void CompileLinesTest::oneLoop() {
auto&& data = OneLoopData[testCaseInstanceId()];
setTestCaseTemplateName(Math::TypeTraits<T>::name());
setTestCaseDescription(data.name);
Containers::Array<T> indices{NoInit, data.indices.size()};
Math::castInto(stridedArrayView(data.indices), stridedArrayView(indices));
Trade::MeshData lineMesh{data.primitive,
{}, indices, indices ? Trade::MeshIndexData{indices} : Trade::MeshIndexData{},
{}, data.positions, {
Trade::MeshAttributeData{Trade::MeshAttribute::Position, stridedArrayView(data.positions)}
}};
Trade::MeshData mesh = Implementation::generateLines(lineMesh);
CORRADE_COMPARE(mesh.primitive(), MeshPrimitive::Triangles);
CORRADE_COMPARE(mesh.attributeCount(), 4);
CORRADE_VERIFY(mesh.isIndexed());
CORRADE_COMPARE(mesh.indexType(), MeshIndexType::UnsignedInt);
if(data.expectedJoins && data.expectedJoinsFirstLast)
CORRADE_COMPARE_AS(mesh.indices<UnsignedInt>(), Containers::arrayView<UnsignedInt>({
2, 0, 1, 1, 3, 2,
2, 3, 4, 4, 3, 5, /* join */
6, 4, 5, 5, 7, 6,
6, 7, 8, 8, 7, 9, /* join */
10, 8, 9, 9, 11, 10,
10, 11, 12, 12, 11, 13, /* join */
14, 12, 13, 13, 15, 14,
14, 15, 0, 0, 15, 1, /* join */
}), TestSuite::Compare::Container);
else if(data.expectedJoins)
CORRADE_COMPARE_AS(mesh.indices<UnsignedInt>(), Containers::arrayView<UnsignedInt>({
2, 0, 1, 1, 3, 2,
2, 3, 4, 4, 3, 5, /* join */
6, 4, 5, 5, 7, 6,
6, 7, 8, 8, 7, 9, /* join */
10, 8, 9, 9, 11, 10,
10, 11, 12, 12, 11, 13, /* join */
14, 12, 13, 13, 15, 14,
}), TestSuite::Compare::Container);
else
CORRADE_COMPARE_AS(mesh.indices<UnsignedInt>(), Containers::arrayView<UnsignedInt>({
2, 0, 1, 1, 3, 2,
6, 4, 5, 5, 7, 6,
10, 8, 9, 9, 11, 10,
14, 12, 13, 13, 15, 14,
}), TestSuite::Compare::Container);
CORRADE_VERIFY(mesh.hasAttribute(Trade::MeshAttribute::Position));
CORRADE_COMPARE(mesh.attributeFormat(Trade::MeshAttribute::Position), VertexFormat::Vector2);
Containers::StridedArrayView1D<const Vector2> positions = mesh.attribute<Vector2>(Trade::MeshAttribute::Position);
CORRADE_COMPARE_AS(positions, Containers::arrayView<Vector2>({
{-1.0f, -1.0f}, {-1.0f, -1.0f},
{+1.0f, -1.0f}, {+1.0f, -1.0f},
{+1.0f, -1.0f}, {+1.0f, -1.0f},
{+1.0f, +1.0f}, {+1.0f, +1.0f},
{+1.0f, +1.0f}, {+1.0f, +1.0f},
{-1.0f, +1.0f}, {-1.0f, +1.0f},
{-1.0f, +1.0f}, {-1.0f, +1.0f},
{-1.0f, -1.0f}, {-1.0f, -1.0f}
}), TestSuite::Compare::Container);
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributePreviousPosition));
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributeNextPosition));
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributeAnnotation));
CORRADE_COMPARE(mesh.attributeFormat(Implementation::MeshAttributePreviousPosition), VertexFormat::Vector2);
CORRADE_COMPARE(mesh.attributeFormat(Implementation::MeshAttributeNextPosition), VertexFormat::Vector2);
CORRADE_COMPARE(mesh.attributeFormat(Implementation::MeshAttributeAnnotation), VertexFormat::UnsignedInt);
if(data.expectedJoins && data.expectedJoinsFirstLast) {
CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::MeshAttributePreviousPosition), Containers::arrayView<Vector2>({
positions[12], positions[12],
positions[0], positions[0],
positions[0], positions[0],
positions[4], positions[4],
positions[4], positions[4],
positions[8], positions[8],
positions[8], positions[8],
positions[12], positions[12],
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::MeshAttributeNextPosition), Containers::arrayView<Vector2>({
positions[2], positions[2],
positions[6], positions[6],
positions[6], positions[6],
positions[10], positions[10],
positions[10], positions[10],
positions[14], positions[14],
positions[14], positions[14],
positions[2], positions[2],
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS((Containers::arrayCast<1, const Shaders::LineVertexAnnotations>(mesh.attribute(Implementation::MeshAttributeAnnotation))), Containers::arrayView<Shaders::LineVertexAnnotations>({
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Join,
}), TestSuite::Compare::Container);
} else if(data.expectedJoins) {
CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::MeshAttributePreviousPosition), Containers::arrayView<Vector2>({
{}, {},
positions[0], positions[0],
positions[0], positions[0],
positions[4], positions[4],
positions[4], positions[4],
positions[8], positions[8],
positions[8], positions[8],
positions[12], positions[12],
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::MeshAttributeNextPosition), Containers::arrayView<Vector2>({
positions[2], positions[2],
positions[6], positions[6],
positions[6], positions[6],
positions[10], positions[10],
positions[10], positions[10],
positions[14], positions[14],
positions[14], positions[14],
{}, {}
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS((Containers::arrayCast<1, const Shaders::LineVertexAnnotations>(mesh.attribute(Implementation::MeshAttributeAnnotation))), Containers::arrayView<Shaders::LineVertexAnnotations>({
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Begin,
Shaders::LineVertexAnnotation::Begin,
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Up,
{},
}), TestSuite::Compare::Container);
} else {
CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::MeshAttributePreviousPosition), Containers::arrayView<Vector2>({
{}, {},
positions[0], positions[0],
{}, {},
positions[4], positions[4],
{}, {},
positions[8], positions[8],
{}, {},
positions[12], positions[12],
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::MeshAttributeNextPosition), Containers::arrayView<Vector2>({
positions[2], positions[2],
{}, {},
positions[6], positions[6],
{}, {},
positions[10], positions[10],
{}, {},
positions[14], positions[14],
{}, {},
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS((Containers::arrayCast<1, const Shaders::LineVertexAnnotations>(mesh.attribute(Implementation::MeshAttributeAnnotation))), Containers::arrayView<Shaders::LineVertexAnnotations>({
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Begin,
Shaders::LineVertexAnnotation::Begin,
Shaders::LineVertexAnnotation::Up,
{},
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Begin,
Shaders::LineVertexAnnotation::Begin,
Shaders::LineVertexAnnotation::Up,
{},
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Begin,
Shaders::LineVertexAnnotation::Begin,
Shaders::LineVertexAnnotation::Up,
{},
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Begin,
Shaders::LineVertexAnnotation::Begin,
Shaders::LineVertexAnnotation::Up,
{},
}), TestSuite::Compare::Container);
}
}
void CompileLinesTest::extraAttributes() {
const struct Vertex {
Color3ub color;
Vector3b position;
UnsignedShort objectId;
} vertexData[]{
{0xdcdcdc_rgb, {-1, -1, 0}, 156},
{0xdcdcdc_rgb, {+1, -1, 1}, 223},
{0x2f83cc_rgb, {+1, +1, 0}, 999},
{0x2f83cc_rgb, {-1, +1, 1}, 768}
};
auto vertices = Containers::stridedArrayView(vertexData);
Trade::MeshData lineMesh{MeshPrimitive::LineLoop,
{}, vertexData, {
/* Having position not first to catch accidental use of first
attribute as position */
Trade::MeshAttributeData{Trade::MeshAttribute::Color, vertices.slice(&Vertex::color)},
Trade::MeshAttributeData{Trade::MeshAttribute::Position, vertices.slice(&Vertex::position)},
Trade::MeshAttributeData{Trade::MeshAttribute::ObjectId, vertices.slice(&Vertex::objectId)},
}};
Trade::MeshData mesh = Implementation::generateLines(lineMesh);
CORRADE_COMPARE(mesh.primitive(), MeshPrimitive::Triangles);
CORRADE_COMPARE(mesh.attributeCount(), 6);
CORRADE_VERIFY(mesh.isIndexed());
CORRADE_COMPARE(mesh.indexType(), MeshIndexType::UnsignedInt);
CORRADE_COMPARE_AS(mesh.indices<UnsignedInt>(), Containers::arrayView<UnsignedInt>({
2, 0, 1, 1, 3, 2,
2, 3, 4, 4, 3, 5, /* join */
6, 4, 5, 5, 7, 6,
6, 7, 8, 8, 7, 9, /* join */
10, 8, 9, 9, 11, 10,
10, 11, 12, 12, 11, 13, /* join */
14, 12, 13, 13, 15, 14,
14, 15, 0, 0, 15, 1, /* join */
}), TestSuite::Compare::Container);
CORRADE_VERIFY(mesh.hasAttribute(Trade::MeshAttribute::Position));
CORRADE_COMPARE(mesh.attributeFormat(Trade::MeshAttribute::Position), VertexFormat::Vector3b);
Containers::StridedArrayView1D<const Vector3b> positions = mesh.attribute<Vector3b>(Trade::MeshAttribute::Position);
CORRADE_COMPARE_AS(positions, Containers::arrayView<Vector3b>({
{-1, -1, 0}, {-1, -1, 0},
{+1, -1, 1}, {+1, -1, 1},
{+1, -1, 1}, {+1, -1, 1},
{+1, +1, 0}, {+1, +1, 0},
{+1, +1, 0}, {+1, +1, 0},
{-1, +1, 1}, {-1, +1, 1},
{-1, +1, 1}, {-1, +1, 1},
{-1, -1, 0}, {-1, -1, 0}
}), TestSuite::Compare::Container);
CORRADE_VERIFY(mesh.hasAttribute(Trade::MeshAttribute::Color));
CORRADE_COMPARE(mesh.attributeFormat(Trade::MeshAttribute::Color), VertexFormat::Vector3ubNormalized);
CORRADE_COMPARE_AS(mesh.attribute<Color3ub>(Trade::MeshAttribute::Color), Containers::arrayView<Color3ub>({
0xdcdcdc_rgb, 0xdcdcdc_rgb,
0xdcdcdc_rgb, 0xdcdcdc_rgb,
0xdcdcdc_rgb, 0xdcdcdc_rgb,
0x2f83cc_rgb, 0x2f83cc_rgb,
0x2f83cc_rgb, 0x2f83cc_rgb,
0x2f83cc_rgb, 0x2f83cc_rgb,
0x2f83cc_rgb, 0x2f83cc_rgb,
0xdcdcdc_rgb, 0xdcdcdc_rgb,
}), TestSuite::Compare::Container);
CORRADE_VERIFY(mesh.hasAttribute(Trade::MeshAttribute::ObjectId));
CORRADE_COMPARE(mesh.attributeFormat(Trade::MeshAttribute::ObjectId), VertexFormat::UnsignedShort);
CORRADE_COMPARE_AS(mesh.attribute<UnsignedShort>(Trade::MeshAttribute::ObjectId), Containers::arrayView<UnsignedShort>({
156, 156,
223, 223,
223, 223,
999, 999,
999, 999,
768, 768,
768, 768,
156, 156,
}), TestSuite::Compare::Container);
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributePreviousPosition));
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributeNextPosition));
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributeAnnotation));
CORRADE_COMPARE(mesh.attributeFormat(Implementation::MeshAttributePreviousPosition), VertexFormat::Vector3b);
CORRADE_COMPARE(mesh.attributeFormat(Implementation::MeshAttributeNextPosition), VertexFormat::Vector3b);
CORRADE_COMPARE(mesh.attributeFormat(Implementation::MeshAttributeAnnotation), VertexFormat::UnsignedInt);
CORRADE_COMPARE_AS(mesh.attribute<Vector3b>(Implementation::MeshAttributePreviousPosition), Containers::arrayView<Vector3b>({
positions[12], positions[12],
positions[0], positions[0],
positions[0], positions[0],
positions[4], positions[4],
positions[4], positions[4],
positions[8], positions[8],
positions[8], positions[8],
positions[12], positions[12],
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(mesh.attribute<Vector3b>(Implementation::MeshAttributeNextPosition), Containers::arrayView<Vector3b>({
positions[2], positions[2],
positions[6], positions[6],
positions[6], positions[6],
positions[10], positions[10],
positions[10], positions[10],
positions[14], positions[14],
positions[14], positions[14],
positions[2], positions[2],
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS((Containers::arrayCast<1, const Shaders::LineVertexAnnotations>(mesh.attribute(Implementation::MeshAttributeAnnotation))), Containers::arrayView<Shaders::LineVertexAnnotations>({
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Join,
}), TestSuite::Compare::Container);
}
void CompileLinesTest::zeroVertices() {
Trade::MeshData lineMesh{MeshPrimitive::LineLoop, nullptr, {
Trade::MeshAttributeData{Trade::MeshAttribute::Position, VertexFormat::Vector3usNormalized, nullptr}
}};
Trade::MeshData mesh = Implementation::generateLines(lineMesh);
CORRADE_COMPARE(mesh.primitive(), MeshPrimitive::Triangles);
CORRADE_COMPARE(mesh.attributeCount(), 4);
CORRADE_COMPARE(mesh.vertexCount(), 0);
CORRADE_VERIFY(mesh.hasAttribute(Trade::MeshAttribute::Position));
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributePreviousPosition));
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributePreviousPosition));
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributeAnnotation));
}
void CompileLinesTest::twoVerticesStrip() {
Vector2 positionData[]{
{-1.0f, 0.0f},
{+1.0f, 0.0f}
};
Trade::MeshData lineMesh{MeshPrimitive::LineStrip, {}, positionData, {
Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::stridedArrayView(positionData)}
}};
Trade::MeshData mesh = Implementation::generateLines(lineMesh);
CORRADE_COMPARE(mesh.primitive(), MeshPrimitive::Triangles);
CORRADE_COMPARE(mesh.attributeCount(), 4);
CORRADE_VERIFY(mesh.isIndexed());
CORRADE_COMPARE(mesh.indexType(), MeshIndexType::UnsignedInt);
CORRADE_COMPARE_AS(mesh.indices<UnsignedInt>(), Containers::arrayView<UnsignedInt>({
2, 0, 1, 1, 3, 2,
}), TestSuite::Compare::Container);
CORRADE_VERIFY(mesh.hasAttribute(Trade::MeshAttribute::Position));
CORRADE_COMPARE(mesh.attributeFormat(Trade::MeshAttribute::Position), VertexFormat::Vector2);
Containers::StridedArrayView1D<const Vector2> positions = mesh.attribute<Vector2>(Trade::MeshAttribute::Position);
CORRADE_COMPARE_AS(positions, Containers::arrayView<Vector2>({
{-1.0f, 0.0f}, {-1.0f, 0.0f},
{+1.0f, 0.0f}, {+1.0f, 0.0f},
}), TestSuite::Compare::Container);
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributePreviousPosition));
CORRADE_COMPARE(mesh.attributeFormat(Implementation::MeshAttributePreviousPosition), VertexFormat::Vector2);
CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::MeshAttributePreviousPosition), Containers::arrayView<Vector2>({
{}, {},
positions[0], positions[0],
}), TestSuite::Compare::Container);
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributePreviousPosition));
CORRADE_COMPARE(mesh.attributeFormat(Implementation::MeshAttributeNextPosition), VertexFormat::Vector2);
CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::MeshAttributeNextPosition), Containers::arrayView<Vector2>({
positions[2], positions[2],
{}, {},
}), TestSuite::Compare::Container);
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributeAnnotation));
CORRADE_COMPARE(mesh.attributeFormat(Implementation::MeshAttributeAnnotation), VertexFormat::UnsignedInt);
CORRADE_COMPARE_AS((Containers::arrayCast<1, const Shaders::LineVertexAnnotations>(mesh.attribute(Implementation::MeshAttributeAnnotation))), Containers::arrayView<Shaders::LineVertexAnnotations>({
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Begin,
Shaders::LineVertexAnnotation::Begin,
Shaders::LineVertexAnnotation::Up,
{},
}), TestSuite::Compare::Container);
}
void CompileLinesTest::twoVerticesLoop() {
Vector2 positionData[]{
{-1.0f, 0.0f},
{+1.0f, 0.0f}
};
Trade::MeshData lineMesh{MeshPrimitive::LineLoop, {}, positionData, {
Trade::MeshAttributeData{Trade::MeshAttribute::Position, Containers::stridedArrayView(positionData)}
}};
Trade::MeshData mesh = Implementation::generateLines(lineMesh);
CORRADE_COMPARE(mesh.primitive(), MeshPrimitive::Triangles);
CORRADE_COMPARE(mesh.attributeCount(), 4);
CORRADE_VERIFY(mesh.isIndexed());
CORRADE_COMPARE(mesh.indexType(), MeshIndexType::UnsignedInt);
CORRADE_COMPARE_AS(mesh.indices<UnsignedInt>(), Containers::arrayView<UnsignedInt>({
2, 0, 1, 1, 3, 2,
2, 3, 4, 4, 3, 5, /* join */
6, 4, 5, 5, 7, 6,
6, 7, 0, 0, 7, 1, /* join */
}), TestSuite::Compare::Container);
CORRADE_VERIFY(mesh.hasAttribute(Trade::MeshAttribute::Position));
CORRADE_COMPARE(mesh.attributeFormat(Trade::MeshAttribute::Position), VertexFormat::Vector2);
Containers::StridedArrayView1D<const Vector2> positions = mesh.attribute<Vector2>(Trade::MeshAttribute::Position);
CORRADE_COMPARE_AS(positions, Containers::arrayView<Vector2>({
{-1.0f, 0.0f}, {-1.0f, 0.0f},
{+1.0f, 0.0f}, {+1.0f, 0.0f},
{+1.0f, 0.0f}, {+1.0f, 0.0f},
{-1.0f, 0.0f}, {-1.0f, 0.0f},
}), TestSuite::Compare::Container);
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributePreviousPosition));
CORRADE_COMPARE(mesh.attributeFormat(Implementation::MeshAttributePreviousPosition), VertexFormat::Vector2);
CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::MeshAttributePreviousPosition), Containers::arrayView<Vector2>({
positions[4], positions[4],
positions[0], positions[0],
positions[0], positions[0],
positions[4], positions[4],
}), TestSuite::Compare::Container);
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributePreviousPosition));
CORRADE_COMPARE(mesh.attributeFormat(Implementation::MeshAttributeNextPosition), VertexFormat::Vector2);
CORRADE_COMPARE_AS(mesh.attribute<Vector2>(Implementation::MeshAttributeNextPosition), Containers::arrayView<Vector2>({
positions[2], positions[2],
positions[6], positions[6],
positions[6], positions[6],
positions[2], positions[2],
}), TestSuite::Compare::Container);
CORRADE_VERIFY(mesh.hasAttribute(Implementation::MeshAttributeAnnotation));
CORRADE_COMPARE(mesh.attributeFormat(Implementation::MeshAttributeAnnotation), VertexFormat::UnsignedInt);
CORRADE_COMPARE_AS((Containers::arrayCast<1, const Shaders::LineVertexAnnotations>(mesh.attribute(Implementation::MeshAttributeAnnotation))), Containers::arrayView<Shaders::LineVertexAnnotations>({
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Begin|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Up|
Shaders::LineVertexAnnotation::Join,
Shaders::LineVertexAnnotation::Join,
}), TestSuite::Compare::Container);
}
}}}}
CORRADE_TEST_MAIN(Magnum::MeshTools::Test::CompileLinesTest)

BIN
src/Magnum/MeshTools/Test/CompileLinesTestFiles/bevel.tga

Binary file not shown.

BIN
src/Magnum/MeshTools/Test/CompileLinesTestFiles/line-primitive.tga

Binary file not shown.

BIN
src/Magnum/MeshTools/Test/CompileLinesTestFiles/miter.tga

Binary file not shown.

BIN
src/Magnum/MeshTools/Test/CompileLinesTestFiles/vertex-color.tga

Binary file not shown.

6
src/Magnum/Shaders/LineGL.h

@ -84,7 +84,11 @@ Instead, it requires the input data to be organized in quads, with
describing point wíth their surroundings, and @ref Annotation with describing point wíth their surroundings, and @ref Annotation with
point-specific annotation such as whether given point is a line cap or a join point-specific annotation such as whether given point is a line cap or a join
with neighboring segment. The data layout is described in detail in with neighboring segment. The data layout is described in detail in
@ref Shaders-LineGL-mesh-representation below. @ref Shaders-LineGL-mesh-representation below, however in practice it's easiest
to compile an existing line @ref Trade::MeshData to the desired form using
@ref MeshTools::compileLines():
@snippet MagnumShaders-gl.cpp LineGL-usage
For rendering use @ref setTransformationProjectionMatrix(), For rendering use @ref setTransformationProjectionMatrix(),
@ref setColor(), @ref setWidth() and others. It's important to pass viewport @ref setColor(), @ref setWidth() and others. It's important to pass viewport

2
src/Magnum/Shaders/Test/LineGLTest.cpp

@ -949,7 +949,7 @@ void LineGLTest::renderTeardown() {
} }
/* A barebones utility for generating a line mesh. Embedded directly in the /* A barebones utility for generating a line mesh. Embedded directly in the
test (as opposed to using something from MeshTools) to have it easier to test (as opposed to using MeshTools::compileLines()) to have it easier to
modify, debug and iterate on. */ modify, debug and iterate on. */
template<UnsignedInt dimensions> struct Vertex { template<UnsignedInt dimensions> struct Vertex {
VectorTypeFor<dimensions, Float> previousPosition; VectorTypeFor<dimensions, Float> previousPosition;

Loading…
Cancel
Save