Browse Source

MeshTools: handle JointIds and Weights in compile().

And also add a compiledPerVertexJointCount() helper which returns the
amount of components used in the primary and secondary joint IDs /
weights slots.

Co-authored-by: Squareys <squareys@googlemail.com>
pull/499/head
Vladimír Vondruš 3 years ago
parent
commit
c026ac1cb5
  1. 184
      src/Magnum/MeshTools/Compile.cpp
  2. 48
      src/Magnum/MeshTools/Compile.h
  3. 1
      src/Magnum/MeshTools/Test/CMakeLists.txt
  4. 310
      src/Magnum/MeshTools/Test/CompileGLTest.cpp
  5. BIN
      src/Magnum/MeshTools/Test/CompileTestFiles/skinning.tga

184
src/Magnum/MeshTools/Compile.cpp

@ -26,6 +26,7 @@
#include "Compile.h"
#include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/Pair.h>
#include <Corrade/Containers/StaticArray.h>
#include <Corrade/Containers/StridedArrayView.h>
@ -68,15 +69,50 @@ GL::Mesh compileInternal(const Trade::MeshData& meshData, GL::Buffer&& indices,
/* Vertex data */
GL::Buffer verticesRef = GL::Buffer::wrap(vertices.id(), GL::Buffer::TargetHint::Array);
/* Ensure each known attribute gets bound only once. There's 16 generic
attributes at most, for each remember the mesh attribute index that got
bound to it first, or ~UnsignedInt{} if none yet. */
/* Except for joint IDs and weights which are treated separately and can
have a secondary set, ensure each known attribute gets bound only once.
There's 16 generic attributes at most, for each remember the mesh
attribute index that got bound to it first, or ~UnsignedInt{} if none
yet. */
/** @todo revisit when there are secondary generic texture coordinates,
colors, etc */
Containers::StaticArray<16, UnsignedInt> boundAttributes{DirectInit, ~UnsignedInt{}};
#ifndef MAGNUM_TARGET_GLES2
UnsignedInt jointIdAttributeCount = 0;
UnsignedInt weightAttributeCount = 0;
#endif
for(UnsignedInt i = 0; i != meshData.attributeCount(); ++i) {
Containers::Optional<GL::DynamicAttribute> attribute;
auto addAttribute = [&](GL::DynamicAttribute attribute, std::size_t offset) {
/* Ensure each attribute gets bound only once -- so for example
when there are two texture coordinate sets, we don't bind them
both to the same slot, effectively ignoring the first one.
Similarly warn if an attribute has a location conflicting with
another one (such as ObjectId and Bitangent). */
if(boundAttributes[attribute.location()] != ~UnsignedInt{}) {
Warning{} << "MeshTools::compile(): ignoring" << meshData.attributeName(i) << meshData.attributeId(i) << "as its biding slot is already occupied by" << meshData.attributeName(boundAttributes[attribute.location()]) << meshData.attributeId(boundAttributes[attribute.location()]);
return;
}
/* Remeber where this attribute got bound, including all subsequent
vectors for matrix attributes */
for(UnsignedInt j = 0; j != attribute.vectors(); ++j)
boundAttributes[attribute.location() + j] = i;
/* Negative strides are not supported by GL, zero strides are
understood as tightly packed instead of all attributes having
the same value */
const Int stride = meshData.attributeStride(i);
CORRADE_ASSERT(stride > 0,
"MeshTools::compile():" << meshData.attributeName(i) << "stride of" << stride << "bytes isn't supported by OpenGL", );
/* 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) + offset, stride, attribute);
else mesh.addVertexBuffer(verticesRef, meshData.attributeOffset(i) + offset,
stride, attribute);
};
/* Ignore implementation-specific formats because GL needs three
separate values to describe them so there's no way to put them in a
@ -92,85 +128,101 @@ GL::Mesh compileInternal(const Trade::MeshData& meshData, GL::Buffer&& indices,
case Trade::MeshAttribute::Position:
/* Pick 3D position always, the format will properly reduce it
to a 2-component version if needed */
attribute.emplace(Shaders::GenericGL3D::Position{}, format);
break;
addAttribute(GL::DynamicAttribute{Shaders::GenericGL3D::Position{}, format}, 0);
continue;
case Trade::MeshAttribute::TextureCoordinates:
/** @todo have GenericGL2D derived from Generic that has all
attribute definitions common for 2D and 3D */
attribute.emplace(Shaders::GenericGL2D::TextureCoordinates{}, format);
break;
addAttribute(GL::DynamicAttribute{Shaders::GenericGL2D::TextureCoordinates{}, format}, 0);
continue;
case Trade::MeshAttribute::Color:
/** @todo have GenericGL2D derived from Generic that has all
attribute definitions common for 2D and 3D */
/* Pick Color4 always, the format will properly reduce it to a
3-component version if needed */
attribute.emplace(Shaders::GenericGL2D::Color4{}, format);
break;
addAttribute(GL::DynamicAttribute{Shaders::GenericGL2D::Color4{}, format}, 0);
continue;
case Trade::MeshAttribute::Tangent:
/* Pick Tangent4 always, the format will properly reduce it to
a 3-component version if needed */
attribute.emplace(Shaders::GenericGL3D::Tangent4{}, format);
break;
addAttribute(GL::DynamicAttribute{Shaders::GenericGL3D::Tangent4{}, format}, 0);
continue;
case Trade::MeshAttribute::Bitangent:
attribute.emplace(Shaders::GenericGL3D::Bitangent{}, format);
break;
addAttribute(GL::DynamicAttribute{Shaders::GenericGL3D::Bitangent{}, format}, 0);
continue;
case Trade::MeshAttribute::Normal:
attribute.emplace(Shaders::GenericGL3D::Normal{}, format);
break;
addAttribute(GL::DynamicAttribute{Shaders::GenericGL3D::Normal{}, format}, 0);
continue;
#ifndef MAGNUM_TARGET_GLES2
case Trade::MeshAttribute::JointIds: {
const UnsignedInt componentCount = meshData.attributeArraySize(i);
const std::size_t componentSize = vertexFormatSize(format);
for(UnsignedInt j = 0; j < componentCount; j += 4) {
const VertexFormat arrayFormat = vertexFormat(format, Math::min(componentCount - j, 4u), false);
/** @todo have GenericGL2D derived from Generic that has
all attribute definitions common for 2D and 3D */
if(jointIdAttributeCount == 0)
addAttribute(GL::DynamicAttribute{Shaders::GenericGL2D::JointIds{}, arrayFormat}, j*componentSize);
else if(jointIdAttributeCount == 1)
addAttribute(GL::DynamicAttribute{Shaders::GenericGL2D::SecondaryJointIds{}, arrayFormat}, j*componentSize);
else {
Warning w;
w << "MeshTools::compile(): ignoring";
if(j != 0)
w << "remaining" << componentCount - j << "components of";
w << "joint ID / weights attribute" << meshData.attributeId(i) << Debug::nospace << ", only two sets are supported at most";
break;
}
++jointIdAttributeCount;
}
} continue;
case Trade::MeshAttribute::Weights: {
const UnsignedInt componentCount = meshData.attributeArraySize(i);
const std::size_t componentSize = vertexFormatSize(format);
for(UnsignedInt j = 0; j < componentCount; j += 4) {
const VertexFormat arrayFormat = vertexFormat(format, Math::min(componentCount - j, 4u), isVertexFormatNormalized(format));
/** @todo have GenericGL2D derived from Generic that has
all attribute definitions common for 2D and 3D */
if(weightAttributeCount == 0)
addAttribute(GL::DynamicAttribute{Shaders::GenericGL2D::Weights{}, arrayFormat}, j*componentSize);
else if(weightAttributeCount == 1)
addAttribute(GL::DynamicAttribute{Shaders::GenericGL2D::SecondaryWeights{}, arrayFormat}, j*componentSize);
else {
/* Warning printed for joints already, the mesh is
expected to have the same count of both so the
warning would be redundant */
break;
}
++weightAttributeCount;
}
} continue;
case Trade::MeshAttribute::ObjectId:
/** @todo have GenericGL2D derived from Generic that has all
attribute definitions common for 2D and 3D */
attribute.emplace(Shaders::GenericGL2D::ObjectId{}, format);
break;
addAttribute(GL::DynamicAttribute{Shaders::GenericGL2D::ObjectId{}, format}, 0);
continue;
#endif
/* To avoid the compiler warning that we didn't handle an enum
value. For these a runtime warning is printed below. */
/* LCOV_EXCL_START */
#ifdef MAGNUM_TARGET_GLES2
case Trade::MeshAttribute::ObjectId:
#endif
case Trade::MeshAttribute::JointIds:
case Trade::MeshAttribute::Weights:
break;
#endif
/* LCOV_EXCL_STOP */
}
if(!attribute) {
if(!Trade::isMeshAttributeCustom(meshData.attributeName(i)) || !(flags & CompileFlag::NoWarnOnCustomAttributes))
Warning{} << "MeshTools::compile(): ignoring unknown/unsupported attribute" << meshData.attributeName(i);
continue;
}
/* Ensure each attribute slot gets bound only once -- so for example
when there are two texture coordinate sets, we don't bind them both
to the same slot, effectively ignoring the first one. Similarly
warn if an attribute has a location conflicting with another one
(such as ObjectId and Bitangent). */
if(boundAttributes[attribute->location()] != ~UnsignedInt{}) {
Warning{} << "MeshTools::compile(): ignoring" << meshData.attributeName(i) << meshData.attributeId(i) << "as its biding slot is already occupied by" << meshData.attributeName(boundAttributes[attribute->location()]) << meshData.attributeId(boundAttributes[attribute->location()]);
continue;
}
/* Remeber where this attribute got bound, including all subsequent
vectors for matrix attributes */
for(UnsignedInt j = 0; j != attribute->vectors(); ++j)
boundAttributes[attribute->location() + j] = i;
/* Negative strides are not supported by GL, zero strides are
understood as tightly packed instead of all attributes having the
same value */
const Int stride = meshData.attributeStride(i);
CORRADE_ASSERT(stride > 0,
"MeshTools::compile():" << meshData.attributeName(i) << "stride of" << stride << "bytes isn't supported by OpenGL", GL::Mesh{});
/* 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), stride, *attribute);
else mesh.addVertexBuffer(verticesRef, meshData.attributeOffset(i),
stride, *attribute);
/* If we got here, the attribute was not recognized */
if(!Trade::isMeshAttributeCustom(meshData.attributeName(i)) || !(flags & CompileFlag::NoWarnOnCustomAttributes))
Warning{} << "MeshTools::compile(): ignoring unknown/unsupported attribute" << meshData.attributeName(i);
}
#ifndef MAGNUM_TARGET_GLES2
CORRADE_INTERNAL_ASSERT(jointIdAttributeCount == weightAttributeCount);
#endif
if(meshData.isIndexed()) {
/* If the type is implementation-specific, we have no way to know if
it's strided, so just assume it is */
@ -501,4 +553,28 @@ GL::Mesh compile(const Trade::MeshData3D& meshData, CompileFlags flags) {
CORRADE_IGNORE_DEPRECATED_POP
#endif
#ifndef MAGNUM_TARGET_GLES2
Containers::Pair<UnsignedInt, UnsignedInt> compiledPerVertexJointCount(const Trade::MeshData& meshData) {
UnsignedInt primaryCount = 0, secondaryCount = 0;
for(UnsignedInt i = 0; i != meshData.attributeCount(); ++i) {
/* The mesh is expected to have the same count and array size of
JointIds and Weights, so it's enough to do it just for one of
them */
if(meshData.attributeName(i) != Trade::MeshAttribute::JointIds)
continue;
const UnsignedInt componentCount = meshData.attributeArraySize(i);
for(UnsignedInt i = 0; i < componentCount; i += 4) {
if(!primaryCount)
primaryCount = Math::min(componentCount - i, 4u);
else if(!secondaryCount)
secondaryCount = Math::min(componentCount - i, 4u);
else break;
}
}
return {primaryCount, secondaryCount};
}
#endif
}}

48
src/Magnum/MeshTools/Compile.h

@ -121,6 +121,11 @@ possibly also an index buffer, if the mesh is indexed.
- If the mesh contains @ref Trade::MeshAttribute::Color, these are bound to
@ref Shaders::GenericGL::Color3 / @relativeref{Shaders::GenericGL,Color4}
based on their type.
- If the mesh contains @ref Trade::MeshAttribute::JointIds and
@ref Trade::MeshAttribute::Weights, these are bound to
@ref Shaders::GenericGL::JointIds / @relativeref{Shaders::GenericGL,SecondaryJointIds} and
@relativeref{Shaders::GenericGL,Weights} / @relativeref{Shaders::GenericGL,SecondaryWeights} according to rules
described in @ref compiledPerVertexJointCount().
- If the mesh contains @ref Trade::MeshAttribute::ObjectId, these are bound
to @ref Shaders::GenericGL::ObjectId. However, if the mesh contains a
@ref Trade::MeshAttribute::Bitangent as well, only the first appearing of
@ -284,6 +289,49 @@ CORRADE_DEPRECATED("use compile(const Trade::MeshData&, CompileFlags) instead")
CORRADE_IGNORE_DEPRECATED_POP
#endif
#ifndef MAGNUM_TARGET_GLES2
/**
@brief Compiled per-vertex joint count for given mesh data
@m_since_latest
Returns the count of bound primary and secondary per-vertex joint IDs and
weights that a mesh returned from @ref compile(const Trade::MeshData&, CompileFlags)
would contain. The function goes over all @ref Trade::MeshAttribute::JointIds
and @relativeref{Trade::MeshAttribute,Weights} attributes present in the mesh
and assigns them to the primary and secondary binding points:
- If the mesh contains just one instance of joint ID and weight attributes
and their @ref Trade::MeshData::attributeArraySize() is not larger than
4, they occupy just the primary binding slot. The second returned value
is @cpp 0 @ce.
- If the mesh contains more than one instance of joint ID and weight
attributes and array size of the first instance is not larger than 4, the
first instance goes to the primary binding slot and the first up to 4
array components of the second instance go to the secondary slot. Remaining
array components of the second instance and all remaining instances of
joint ID and weight attributes are ignored.
- If array size of the first instance of joint ID and weight attributes is
larger than 4, the first slot uses the first 4 array components and the
second the next up to 4 array components. Remaining array components of the
first instance and all remaining instances of joint ID and weight
attributes are ignored.
Useful to get subsequently fed to
@ref Shaders::FlatGL::Configuration::setJointCount() or to
@ref Shaders::FlatGL::setPerVertexJointCount() if
@ref Shaders::FlatGL::Flag::DynamicPerVertexJointCount is enabled, and
similarly with other builtin shaders.
@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 Not defined on OpenGL ES 2.0 builds.
@requires_webgl20 Not defined on WebGL 1.0 builds.
*/
MAGNUM_MESHTOOLS_EXPORT Containers::Pair<UnsignedInt, UnsignedInt> compiledPerVertexJointCount(const Trade::MeshData& meshData);
#endif
}}
#else
#error this header is available only in the OpenGL build

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

@ -105,6 +105,7 @@ if(MAGNUM_BUILD_GL_TESTS)
CompileTestFiles/phong.tga
CompileTestFiles/phong-smooth.tga
CompileTestFiles/phong-flat.tga
CompileTestFiles/skinning.tga
CompileTestFiles/textured2D.tga
CompileTestFiles/textured3D.tga)
target_include_directories(MeshToolsCompileGLTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>)

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

@ -26,6 +26,7 @@
#include <sstream>
#include <Corrade/Containers/EnumSet.h>
#include <Corrade/Containers/GrowableArray.h>
#include <Corrade/Containers/Pair.h>
#include <Corrade/Containers/String.h>
#include <Corrade/Containers/StringIterable.h>
#include <Corrade/PluginManager/Manager.h>
@ -48,6 +49,8 @@
#include "Magnum/GL/Texture.h"
#include "Magnum/GL/TextureFormat.h"
#include "Magnum/Math/Color.h"
#include "Magnum/Math/Half.h"
#include "Magnum/Math/Matrix3.h"
#include "Magnum/Math/Matrix4.h"
#include "Magnum/MeshTools/Compile.h"
#include "Magnum/MeshTools/Duplicate.h"
@ -106,6 +109,12 @@ struct CompileGLTest: GL::OpenGLTester {
void packedAttributes();
#ifndef MAGNUM_TARGET_GLES2
/* Tests also compiledPerVertexJointCount() */
void skinning();
void skinningPackedAttributes();
#endif
void conflictingAttributes();
void unsupportedIndexStride();
@ -203,6 +212,138 @@ const struct {
{"positions, object id, nonindexed", Flag::ObjectId|Flag::NonIndexed, {}}
};
constexpr std::ptrdiff_t SkinningDataStride = sizeof(Vector2) + 7*sizeof(UnsignedInt) + 7*sizeof(Float);
const struct {
const char* name;
Containers::Array<Trade::MeshAttributeData> attributes;
UnsignedInt expectedJointCount, expectedSecondaryJointCount;
const char* expectedMessage;
} SkinningData[]{
/* First two and last two weights are zeros thus the middle 3 components
are enough to give the full output */
{"single 3-component attribute", {InPlaceInit, {
Trade::MeshAttributeData{Trade::MeshAttribute::JointIds,
VertexFormat::UnsignedInt,
sizeof(Vector2) + 2*sizeof(UnsignedInt),
4, SkinningDataStride, 3},
Trade::MeshAttributeData{Trade::MeshAttribute::Weights,
VertexFormat::Float,
sizeof(Vector2) + 7*sizeof(UnsignedInt) + 2*sizeof(Float),
4, SkinningDataStride, 3},
}}, 3, 0, ""},
{"single 7-component attribute", {InPlaceInit, {
Trade::MeshAttributeData{Trade::MeshAttribute::JointIds,
VertexFormat::UnsignedInt,
sizeof(Vector2),
4, SkinningDataStride, 7},
Trade::MeshAttributeData{Trade::MeshAttribute::Weights,
VertexFormat::Float,
sizeof(Vector2) + 7*sizeof(UnsignedInt),
4, SkinningDataStride, 7},
}}, 4, 3, ""},
{"3-component and a 4-component attribute", {InPlaceInit, {
Trade::MeshAttributeData{Trade::MeshAttribute::JointIds,
VertexFormat::UnsignedInt,
sizeof(Vector2),
4, SkinningDataStride, 3},
Trade::MeshAttributeData{Trade::MeshAttribute::Weights,
VertexFormat::Float,
sizeof(Vector2) + 7*sizeof(UnsignedInt),
4, SkinningDataStride, 3},
Trade::MeshAttributeData{Trade::MeshAttribute::JointIds,
VertexFormat::UnsignedInt,
sizeof(Vector2) + 3*sizeof(UnsignedInt),
4, SkinningDataStride, 4},
Trade::MeshAttributeData{Trade::MeshAttribute::Weights,
VertexFormat::Float,
sizeof(Vector2) + 7*sizeof(UnsignedInt) + 3*sizeof(Float),
4, SkinningDataStride, 4},
}}, 3, 4, ""},
{"4-component and a 3-component attribute", {InPlaceInit, {
Trade::MeshAttributeData{Trade::MeshAttribute::JointIds,
VertexFormat::UnsignedInt,
sizeof(Vector2),
4, SkinningDataStride, 4},
Trade::MeshAttributeData{Trade::MeshAttribute::Weights,
VertexFormat::Float,
sizeof(Vector2) + 7*sizeof(UnsignedInt),
4, SkinningDataStride, 4},
Trade::MeshAttributeData{Trade::MeshAttribute::JointIds,
VertexFormat::UnsignedInt,
sizeof(Vector2) + 4*sizeof(UnsignedInt),
4, SkinningDataStride, 3},
Trade::MeshAttributeData{Trade::MeshAttribute::Weights,
VertexFormat::Float,
sizeof(Vector2) + 7*sizeof(UnsignedInt) + 4*sizeof(Float),
4, SkinningDataStride, 3},
}}, 4, 3, ""},
{"1-component and a 5-component attribute", {InPlaceInit, {
Trade::MeshAttributeData{Trade::MeshAttribute::JointIds,
VertexFormat::UnsignedInt,
sizeof(Vector2) + 1*sizeof(UnsignedInt),
4, SkinningDataStride, 1},
Trade::MeshAttributeData{Trade::MeshAttribute::Weights,
VertexFormat::Float,
sizeof(Vector2) + 7*sizeof(UnsignedInt) + 1*sizeof(Float),
4, SkinningDataStride, 1},
Trade::MeshAttributeData{Trade::MeshAttribute::JointIds,
VertexFormat::UnsignedInt,
sizeof(Vector2) + 2*sizeof(UnsignedInt),
4, SkinningDataStride, 5},
Trade::MeshAttributeData{Trade::MeshAttribute::Weights,
VertexFormat::Float,
sizeof(Vector2) + 7*sizeof(UnsignedInt) + 2*sizeof(Float),
4, SkinningDataStride, 5},
}}, 1, 4, "MeshTools::compile(): ignoring remaining 1 components of joint ID / weights attribute 1, only two sets are supported at most\n"},
{"3-component, 2-component and a 2-component attribute", {InPlaceInit, {
Trade::MeshAttributeData{Trade::MeshAttribute::JointIds,
VertexFormat::UnsignedInt,
sizeof(Vector2),
4, SkinningDataStride, 3},
Trade::MeshAttributeData{Trade::MeshAttribute::Weights,
VertexFormat::Float,
sizeof(Vector2) + 7*sizeof(UnsignedInt),
4, SkinningDataStride, 3},
Trade::MeshAttributeData{Trade::MeshAttribute::JointIds,
VertexFormat::UnsignedInt,
sizeof(Vector2) + 3*sizeof(UnsignedInt),
4, SkinningDataStride, 2},
Trade::MeshAttributeData{Trade::MeshAttribute::Weights,
VertexFormat::Float,
sizeof(Vector2) + 7*sizeof(UnsignedInt) + 3*sizeof(Float),
4, SkinningDataStride, 2},
Trade::MeshAttributeData{Trade::MeshAttribute::JointIds,
VertexFormat::UnsignedInt,
sizeof(Vector2) + 5*sizeof(UnsignedInt),
4, SkinningDataStride, 2},
Trade::MeshAttributeData{Trade::MeshAttribute::Weights,
VertexFormat::Float,
sizeof(Vector2) + 7*sizeof(UnsignedInt) + 5*sizeof(Float),
4, SkinningDataStride, 2},
}}, 3, 2, "MeshTools::compile(): ignoring joint ID / weights attribute 2, only two sets are supported at most\n"},
{"single 7-component attribute, and then a 9-component attribute", {InPlaceInit, {
Trade::MeshAttributeData{Trade::MeshAttribute::JointIds,
VertexFormat::UnsignedInt,
sizeof(Vector2),
4, SkinningDataStride, 7},
Trade::MeshAttributeData{Trade::MeshAttribute::Weights,
VertexFormat::Float,
sizeof(Vector2) + 7*sizeof(UnsignedInt),
4, SkinningDataStride, 7},
Trade::MeshAttributeData{Trade::MeshAttribute::JointIds,
VertexFormat::UnsignedInt,
0,
4, SkinningDataStride, 9},
Trade::MeshAttributeData{Trade::MeshAttribute::Weights,
VertexFormat::Float,
0,
4, SkinningDataStride, 9},
/* The warning should be printed just once for the whole attribute, not
again for every group of four components */
}}, 4, 3, "MeshTools::compile(): ignoring joint ID / weights attribute 1, only two sets are supported at most\n"},
};
constexpr std::ptrdiff_t ConflictingAttributesDataStride = 3*sizeof(Vector2) + sizeof(UnsignedInt) + sizeof(Vector3);
const struct {
@ -243,6 +384,7 @@ const struct {
}}, Flag::ObjectId, 26234, "flat2D.tga",
"ignoring Trade::MeshAttribute::Bitangent 0 as its biding slot is already occupied by Trade::MeshAttribute::ObjectId 0"},
#endif
/* Conflicting skinning attributes tested directly in skinning() */
/** @todo test also a conflict with instanced transformation + secondary
joints & weights, once instanced transformation is a builtin
attribute */
@ -313,6 +455,17 @@ CompileGLTest::CompileGLTest() {
&CompileGLTest::renderSetup,
&CompileGLTest::renderTeardown);
#ifndef MAGNUM_TARGET_GLES2
addInstancedTests({&CompileGLTest::skinning},
Containers::arraySize(SkinningData),
&CompileGLTest::renderSetup,
&CompileGLTest::renderTeardown);
addTests({&CompileGLTest::skinningPackedAttributes},
&CompileGLTest::renderSetup,
&CompileGLTest::renderTeardown);
#endif
addInstancedTests({&CompileGLTest::conflictingAttributes},
Containers::arraySize(ConflictingAttributesData),
&CompileGLTest::renderSetup,
@ -1140,6 +1293,163 @@ void CompileGLTest::packedAttributes() {
#endif
}
#ifndef MAGNUM_TARGET_GLES2
void CompileGLTest::skinning() {
auto&& data = SkinningData[testCaseInstanceId()];
setTestCaseDescription(data.name);
/* Same as in FlatGLTest/PhongGLTest/MeshVisualizerGLTest, just padded
with two dummy values at the beginning and at the end */
struct Vertex {
Vector2 position;
UnsignedInt jointIds[7];
Float weights[7];
} vertexData[]{
/* Top right corner gets moved to the right and up, top left just up,
bottom right just right, bottom left corner gets slightly scaled.
3--1
| /|
|/ |
2--0 */
{{ 1.0f, -1.0f},
{0, 0, 0, 2, 0, 0, 0},
{0.0f, 0.0f, 1.0f, 50.0f, 0.5f, 0.0f, 0.0f}},
{{ 1.0f, 1.0f},
{0, 0, 1, 0, 0, 0, 0},
{0.0f, 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f}},
{{-1.0f, -1.0f},
{0, 0, 3, 4, 4, 0, 0},
{0.0f, 0.0f, 0.5f, 0.25f, 0.25f, 0.0f, 0.0f}},
{{-1.0f, 1.0f},
{0, 0, 1, 0, 4, 0, 0},
{0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}},
};
static_assert(sizeof(Vertex) == SkinningDataStride, "");
auto vertices = Containers::stridedArrayView(vertexData);
Matrix3 jointMatrices[]{
Matrix3::translation(Vector2::xAxis(0.5f)),
Matrix3::translation(Vector2::yAxis(0.5f)),
Matrix3{Math::ZeroInit},
Matrix3::scaling(Vector2{2.0f}),
Matrix3{Math::IdentityInit},
};
Containers::Array<Trade::MeshAttributeData> attributeData;
arrayAppend(attributeData, InPlaceInit, Trade::MeshAttribute::Position,
vertices.slice(&Vertex::position));
arrayAppend(attributeData, data.attributes);
Trade::MeshData meshData{MeshPrimitive::TriangleStrip, {}, vertexData, std::move(attributeData)};
Containers::Pair<UnsignedInt, UnsignedInt> jointCount = compiledPerVertexJointCount(meshData);
CORRADE_COMPARE(jointCount.first(), data.expectedJointCount);
CORRADE_COMPARE(jointCount.second(), data.expectedSecondaryJointCount);
GL::Mesh mesh{NoCreate};
std::ostringstream out;
{
Warning redirectWarning{&out};
mesh = compile(meshData);
}
CORRADE_COMPARE(out.str(), data.expectedMessage);
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);
Shaders::FlatGL2D shader{Shaders::FlatGL2D::Configuration{}
.setJointCount(Containers::arraySize(jointMatrices), jointCount.first(), jointCount.second())};
shader.setJointMatrices(jointMatrices)
.setTransformationProjectionMatrix(Matrix3::scaling(Vector2{0.5f}))
.draw(mesh);
MAGNUM_VERIFY_NO_GL_ERROR();
CORRADE_COMPARE_WITH(
_framebuffer.read({{}, {32, 32}}, {PixelFormat::RGBA8Unorm}),
Utility::Path::join(MESHTOOLS_TEST_DIR, "CompileTestFiles/skinning.tga"),
(DebugTools::CompareImageToFile{_manager}));
}
void CompileGLTest::skinningPackedAttributes() {
/* Same as skinning(), just with the attributes packed, and packed
differently for each set; and using a 3D shader */
struct Vertex {
Vector2 position;
UnsignedShort jointIds[4];
UnsignedByte secondaryJointIds[3];
Float weights[4];
Half secondaryWeights[3];
} vertexData[]{
{{ 1.0f, -1.0f},
{0, 0, 0, 2}, {0, 0, 0},
{0.0f, 0.0f, 1.0f, 50.0f}, { 0.5_h, 0.0_h, 0.0_h}},
{{ 1.0f, 1.0f},
{0, 0, 1, 0}, {0, 0, 0},
{0.0f, 0.0f, 0.5f, 0.5f}, { 0.0_h, 0.0_h, 0.0_h}},
{{-1.0f, -1.0f},
{0, 0, 3, 4}, {4, 0, 0},
{0.0f, 0.0f, 0.5f, 0.25f}, {0.25_h, 0.0_h, 0.0_h}},
{{-1.0f, 1.0f},
{0, 0, 1, 0}, {4, 0, 0},
{0.0f, 0.0f, 1.0f, 0.0f}, { 0.0_h, 0.0_h, 0.0_h}},
};
auto vertices = Containers::stridedArrayView(vertexData);
Matrix4 jointMatrices[]{
Matrix4::translation(Vector3::xAxis(0.5f)),
Matrix4::translation(Vector3::yAxis(0.5f)),
Matrix4{Math::ZeroInit},
Matrix4::scaling(Vector3{2.0f}),
Matrix4{Math::IdentityInit},
};
Trade::MeshData meshData{MeshPrimitive::TriangleStrip, {}, vertexData, {
Trade::MeshAttributeData{Trade::MeshAttribute::Position,
vertices.slice(&Vertex::position)},
Trade::MeshAttributeData{Trade::MeshAttribute::JointIds,
VertexFormat::UnsignedShort,
vertices.slice(&Vertex::jointIds), 4},
Trade::MeshAttributeData{Trade::MeshAttribute::JointIds,
VertexFormat::UnsignedByte,
vertices.slice(&Vertex::secondaryJointIds), 3},
Trade::MeshAttributeData{Trade::MeshAttribute::Weights,
VertexFormat::Float,
vertices.slice(&Vertex::weights), 4},
Trade::MeshAttributeData{Trade::MeshAttribute::Weights,
VertexFormat::Half,
vertices.slice(&Vertex::secondaryWeights), 3},
}};
GL::Mesh mesh = compile(meshData);
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);
Shaders::FlatGL3D shader{Shaders::FlatGL3D::Configuration{}
.setJointCount(Containers::arraySize(jointMatrices), 4, 3)};
shader.setJointMatrices(jointMatrices)
.setTransformationProjectionMatrix(Matrix4::scaling(Vector3{0.5f}))
.draw(mesh);
MAGNUM_VERIFY_NO_GL_ERROR();
CORRADE_COMPARE_WITH(
_framebuffer.read({{}, {32, 32}}, {PixelFormat::RGBA8Unorm}),
Utility::Path::join(MESHTOOLS_TEST_DIR, "CompileTestFiles/skinning.tga"),
(DebugTools::CompareImageToFile{_manager}));
}
#endif
void CompileGLTest::conflictingAttributes() {
auto&& data = ConflictingAttributesData[testCaseInstanceId()];
setTestCaseDescription(data.name);

BIN
src/Magnum/MeshTools/Test/CompileTestFiles/skinning.tga

Binary file not shown.
Loading…
Cancel
Save