Skip to content

Commit

Permalink
TinyGltfImporter: support custom attributes.
Browse files Browse the repository at this point in the history
  • Loading branch information
mosra committed Mar 28, 2020
1 parent 93704c3 commit 79910b4
Show file tree
Hide file tree
Showing 13 changed files with 389 additions and 108 deletions.
1 change: 1 addition & 0 deletions doc/changelog-plugins.dox
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ namespace Magnum {
unuspported `KHR_lights_cmn` (see [mosra/magnum-plugins#77](https://github.com/mosra/magnum-plugins/pull/77))
- Support for the `KHR_texture_transform` and `KHR_mesh_quantization`
extensions in @ref Trade::TinyGltfImporter "TinyGltfImporter"
- Custom vertex attribute support in @ref Trade::TinyGltfImporter "TinyGltfImporter"
- @ref Trade::DdsImporter "DdsImporter" and
@ref Trade::BasisImporter "BasisImporter", now support loading image mip
levels; @ref Trade::AssimpImporter "AssimpImporter",
Expand Down
2 changes: 2 additions & 0 deletions src/MagnumPlugins/TinyGltfImporter/Test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ corrade_add_test(TinyGltfImporterTest
mesh.gltf
mesh.bin
mesh.glb
mesh-custom-attributes.bin
mesh-custom-attributes.gltf
mesh-embedded.gltf
mesh-embedded.glb
mesh-index-accessor-oob.gltf
Expand Down
135 changes: 99 additions & 36 deletions src/MagnumPlugins/TinyGltfImporter/Test/TinyGltfImporterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,9 @@ struct TinyGltfImporterTest: TestSuite::Tester {
void meshAttributeless();
void meshIndexed();
void meshIndexedAttributeless();
void meshUnknownAttribute();
void meshColors();
void meshCustomAttributes();
void meshCustomAttributesNoFileOpened();
void meshMultiplePrimitives();
void meshPrimitivesTypes();
/* This is THE ONE AND ONLY OOB check done by tinygltf, so it fails right
Expand Down Expand Up @@ -257,7 +258,9 @@ constexpr struct {
{"buffer view range out of bounds", "bufferView 2 needs 72 bytes but buffer 0 has only 68"},
{"buffer index out of bounds", "buffer 1 out of bounds for 1 buffers"},
{"buffer view index out of bounds", "bufferView 4 out of bounds for 4 views"},
{"accessor index out of bounds", "accessor 15 out of bounds for 15 accessors"}
{"normalized float", "floating-point component types can't be normalized"},
{"non-normalized byte matrix", "unsupported matrix component type unnormalized 5120"},
{"accessor index out of bounds", "accessor 17 out of bounds for 17 accessors"}
};

constexpr struct {
Expand Down Expand Up @@ -447,8 +450,9 @@ TinyGltfImporterTest::TinyGltfImporterTest() {
addTests({&TinyGltfImporterTest::meshAttributeless,
&TinyGltfImporterTest::meshIndexed,
&TinyGltfImporterTest::meshIndexedAttributeless,
&TinyGltfImporterTest::meshUnknownAttribute,
&TinyGltfImporterTest::meshColors,
&TinyGltfImporterTest::meshCustomAttributes,
&TinyGltfImporterTest::meshCustomAttributesNoFileOpened,
&TinyGltfImporterTest::meshMultiplePrimitives});

addInstancedTests({&TinyGltfImporterTest::meshPrimitivesTypes},
Expand Down Expand Up @@ -1555,7 +1559,7 @@ void TinyGltfImporterTest::mesh() {
CORRADE_VERIFY(importer->openFile(Utility::Directory::join(TINYGLTFIMPORTER_TEST_DIR,
"mesh" + std::string{data.suffix})));

CORRADE_COMPARE(importer->meshCount(), 5);
CORRADE_COMPARE(importer->meshCount(), 4);
CORRADE_COMPARE(importer->meshName(0), "Non-indexed mesh");
CORRADE_COMPARE(importer->meshForName("Non-indexed mesh"), 0);

Expand Down Expand Up @@ -1606,7 +1610,7 @@ void TinyGltfImporterTest::meshIndexed() {
CORRADE_VERIFY(importer->openFile(Utility::Directory::join(TINYGLTFIMPORTER_TEST_DIR,
"mesh.gltf")));

CORRADE_COMPARE(importer->meshCount(), 5);
CORRADE_COMPARE(importer->meshCount(), 4);
CORRADE_COMPARE(importer->meshName(1), "Indexed mesh");
CORRADE_COMPARE(importer->meshForName("Indexed mesh"), 1);

Expand Down Expand Up @@ -1657,37 +1661,6 @@ void TinyGltfImporterTest::meshIndexedAttributeless() {
CORRADE_COMPARE(mesh->attributeCount(), 0);
}

void TinyGltfImporterTest::meshUnknownAttribute() {
Containers::Pointer<AbstractImporter> importer = _manager.instantiate("TinyGltfImporter");
CORRADE_VERIFY(importer->openFile(Utility::Directory::join(TINYGLTFIMPORTER_TEST_DIR,
"mesh.gltf")));

CORRADE_COMPARE(importer->meshCount(), 5);
CORRADE_COMPARE(importer->meshName(2), "Mesh with unknown attribute");
CORRADE_COMPARE(importer->meshForName("Mesh with unknown attribute"), 2);

std::ostringstream out;
Warning redirectWarning{&out};

auto mesh = importer->mesh(2);

CORRADE_COMPARE(out.str(), "Trade::TinyGltfImporter::mesh(): unsupported mesh vertex attribute UNKNOWN\n");

CORRADE_VERIFY(mesh);
CORRADE_VERIFY(mesh->importerState());
CORRADE_COMPARE(mesh->primitive(), MeshPrimitive::Triangles);

CORRADE_COMPARE(mesh->attributeCount(), 1);
CORRADE_VERIFY(mesh->hasAttribute(MeshAttribute::Position));
CORRADE_COMPARE(mesh->attributeFormat(MeshAttribute::Position), VertexFormat::Vector3);
CORRADE_COMPARE_AS(mesh->attribute<Vector3>(MeshAttribute::Position),
Containers::arrayView<Vector3>({
{1.5f, -1.0f, -0.5f},
{-0.5f, 2.5f, 0.75f},
{-2.0f, 1.0f, 0.3f}
}), TestSuite::Compare::Container);
}

void TinyGltfImporterTest::meshColors() {
Containers::Pointer<AbstractImporter> importer = _manager.instantiate("TinyGltfImporter");
CORRADE_VERIFY(importer->openFile(Utility::Directory::join(TINYGLTFIMPORTER_TEST_DIR,
Expand Down Expand Up @@ -1724,6 +1697,96 @@ void TinyGltfImporterTest::meshColors() {
}), TestSuite::Compare::Container);
}

void TinyGltfImporterTest::meshCustomAttributes() {
Containers::Pointer<AbstractImporter> importer = _manager.instantiate("TinyGltfImporter");
CORRADE_VERIFY(importer->openFile(Utility::Directory::join(TINYGLTFIMPORTER_TEST_DIR,
"mesh-custom-attributes.gltf")));
CORRADE_COMPARE(importer->meshCount(), 1);

/* The mapping should be available even before the mesh is imported.
Attributes are sorted by name by JSON so the ID isn't in declaration
order. */
const MeshAttribute tbnAttribute = importer->meshAttributeForName("_TBN");
CORRADE_COMPARE(tbnAttribute, meshAttributeCustom(4));
CORRADE_COMPARE(importer->meshAttributeName(tbnAttribute), "_TBN");

const MeshAttribute uvRotation = importer->meshAttributeForName("_UV_ROTATION");
CORRADE_COMPARE(uvRotation, meshAttributeCustom(6));
CORRADE_COMPARE(importer->meshAttributeName(uvRotation), "_UV_ROTATION");

const MeshAttribute tbnPreciserAttribute = importer->meshAttributeForName("_TBN_PRECISER");
const MeshAttribute doubleShotAttribute = importer->meshAttributeForName("_DOUBLE_SHOT");
const MeshAttribute objectIdAttribute = importer->meshAttributeForName("_OBJECT_ID");
const MeshAttribute negativePaddingAttribute = importer->meshAttributeForName("_NEGATIVE_PADDING");
const MeshAttribute notAnIdentityAttribute = importer->meshAttributeForName("_NOT_AN_IDENTITY");

auto mesh = importer->mesh(0);
CORRADE_VERIFY(mesh);

CORRADE_COMPARE(mesh->attributeCount(), 7);
CORRADE_VERIFY(mesh->hasAttribute(tbnAttribute));
CORRADE_COMPARE(mesh->attributeFormat(tbnAttribute), VertexFormat::Matrix3x3bNormalizedAligned);
CORRADE_COMPARE_AS(mesh->attribute<Matrix3x4b>(tbnAttribute),
Containers::arrayView<Matrix3x4b>({{
Vector4b{1, 2, 3, 0},
Vector4b{4, 5, 6, 0},
Vector4b{7, 8, 9, 0}
}}), TestSuite::Compare::Container);

CORRADE_VERIFY(mesh->hasAttribute(uvRotation));
CORRADE_COMPARE(mesh->attributeFormat(uvRotation), VertexFormat::Matrix2x2bNormalizedAligned);
CORRADE_COMPARE_AS(mesh->attribute<Matrix2x4b>(uvRotation),
Containers::arrayView<Matrix2x4b>({{
Vector4b{10, 11, 0, 0},
Vector4b{12, 13, 0, 0},
}}), TestSuite::Compare::Container);

CORRADE_VERIFY(mesh->hasAttribute(tbnPreciserAttribute));
CORRADE_COMPARE(mesh->attributeFormat(tbnPreciserAttribute), VertexFormat::Matrix3x3sNormalizedAligned);
CORRADE_COMPARE_AS(mesh->attribute<Matrix3x4s>(tbnPreciserAttribute),
Containers::arrayView<Matrix3x4s>({{
Vector4s{-1, -2, -3, 0},
Vector4s{-4, -5, -6, 0},
Vector4s{-7, -8, -9, 0}
}}), TestSuite::Compare::Container);

CORRADE_VERIFY(mesh->hasAttribute(doubleShotAttribute));
CORRADE_COMPARE(mesh->attributeFormat(doubleShotAttribute), VertexFormat::Vector2d);
CORRADE_COMPARE_AS(mesh->attribute<Vector2d>(doubleShotAttribute),
Containers::arrayView<Vector2d>({{31.2, 28.8}}),
TestSuite::Compare::Container);

CORRADE_VERIFY(mesh->hasAttribute(objectIdAttribute));
CORRADE_COMPARE(mesh->attributeFormat(objectIdAttribute), VertexFormat::UnsignedInt);
CORRADE_COMPARE_AS(mesh->attribute<UnsignedInt>(objectIdAttribute),
Containers::arrayView<UnsignedInt>({5678125}),
TestSuite::Compare::Container);

CORRADE_VERIFY(mesh->hasAttribute(negativePaddingAttribute));
CORRADE_COMPARE(mesh->attributeFormat(negativePaddingAttribute), VertexFormat::Int);
CORRADE_COMPARE_AS(mesh->attribute<Int>(negativePaddingAttribute),
Containers::arrayView<Int>({-3548415}),
TestSuite::Compare::Container);

CORRADE_VERIFY(mesh->hasAttribute(notAnIdentityAttribute));
CORRADE_COMPARE(mesh->attributeFormat(notAnIdentityAttribute), VertexFormat::Matrix4x4d);
CORRADE_COMPARE_AS(mesh->attribute<Matrix4d>(notAnIdentityAttribute),
Containers::arrayView<Matrix4d>({{
{0.1, 0.2, 0.3, 0.4},
{0.5, 0.6, 0.7, 0.8},
{0.9, 1.0, 1.1, 1.2},
{1.3, 1.4, 1.5, 1.6}
}}), TestSuite::Compare::Container);
}

void TinyGltfImporterTest::meshCustomAttributesNoFileOpened() {
Containers::Pointer<AbstractImporter> importer = _manager.instantiate("TinyGltfImporter");

/* These should return nothing (and not crash) */
CORRADE_COMPARE(importer->meshAttributeName(meshAttributeCustom(564)), "");
CORRADE_COMPARE(importer->meshAttributeForName("thing"), MeshAttribute{});
}

void TinyGltfImporterTest::meshMultiplePrimitives() {
Containers::Pointer<AbstractImporter> importer = _manager.instantiate("TinyGltfImporter");
CORRADE_VERIFY(importer->openFile(Utility::Directory::join(TINYGLTFIMPORTER_TEST_DIR,
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
type = '<'
input = []

# _TBN as bytes, there has to be padding
type += '3bx3bx3bx'
input += [
1, 2, 3,
4, 5, 6,
7, 8, 9
]

# _UV_ROTATION as bytes, again with padding
type += '2bxx2bxx'
input += [
10, 11,
12, 13
]

# _TBN_PRECISER as shorts, again padding
type += '3hxx3hxx3hxx'
input += [
-1, -2, -3,
-4, -5, -6,
-7, -8, -9
]

# _DOUBLE_SHOT, _OBJECT_ID, _NEGATIVE_PADDING
type += 'ddIixxxx'
input += [31.2, 28.8, 5678125, -3548415]

# _NOT_AN_IDENTITY
type += '16d'
input += [
0.1, 0.2, 0.3, 0.4,
0.5, 0.6, 0.7, 0.8,
0.9, 1.0, 1.1, 1.2,
1.3, 1.4, 1.5, 1.6
]

# kate: hl python
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
{
"asset": {
"version": "2.0"
},
"meshes": [
{
"primitives": [
{
"attributes": {
"_TBN": 0,
"_UV_ROTATION": 1,
"_TBN_PRECISER": 2,
"_DOUBLE_SHOT": 3,
"_OBJECT_ID": 4,
"_NEGATIVE_PADDING": 5,
"_NOT_AN_IDENTITY": 6
}
}
]
}
],
"accessors": [
{
"bufferView": 0,
"byteOffset": 0,
"componentType": 5120,
"normalized": true,
"count": 1,
"type": "MAT3"
},
{
"bufferView": 0,
"byteOffset": 12,
"componentType": 5120,
"normalized": true,
"count": 1,
"type": "MAT2"
},
{
"bufferView": 0,
"byteOffset": 20,
"componentType": 5122,
"normalized": true,
"count": 1,
"type": "MAT3"
},
{
"bufferView": 0,
"byteOffset": 44,
"componentType": 5130,
"count": 1,
"type": "VEC2"
},
{
"bufferView": 0,
"byteOffset": 60,
"componentType": 5125,
"count": 1,
"type": "SCALAR"
},
{
"bufferView": 0,
"byteOffset": 64,
"componentType": 5124,
"count": 1,
"type": "SCALAR"
},
{
"bufferView": 0,
"byteOffset": 72,
"componentType": 5130,
"count": 1,
"type": "MAT4"
}
],
"bufferViews": [
{
"buffer": 0,
"byteLength": 200,
"byteStride": 200
}
],
"buffers": [
{
"byteLength": 200,
"uri": "mesh-custom-attributes.bin"
}
]
}
Binary file modified src/MagnumPlugins/TinyGltfImporter/Test/mesh-embedded.glb
Binary file not shown.
2 changes: 1 addition & 1 deletion src/MagnumPlugins/TinyGltfImporter/Test/mesh-embedded.gltf
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"asset":{"version":"2.0"},"meshes":[{"name":"Non-indexed mesh","primitives":[{"attributes":{"POSITION":1,"TEXCOORD_0":3}}]},{"name":"Indexed mesh","primitives":[{"attributes":{"NORMAL":2,"POSITION":1},"indices":0}]},{"name":"Mesh with unknown attribute","primitives":[{"attributes":{"POSITION":1,"UNKNOWN":1}}]},{"name":"Attribute-less mesh","primitives":[{"attributes":{}}]},{"name":"Attribute-less indexed mesh","primitives":[{"attributes":{},"indices":0}]}],"accessors":[{"bufferView":0,"componentType":5121,"count":3,"type":"SCALAR"},{"bufferView":1,"componentType":5126,"count":3,"type":"VEC3"},{"bufferView":1,"byteOffset":12,"componentType":5126,"count":3,"type":"VEC3"},{"bufferView":2,"componentType":5126,"count":3,"type":"VEC2"}],"bufferViews":[{"buffer":0,"byteLength":3,"byteOffset":0,"target":34963},{"buffer":0,"byteLength":72,"byteOffset":4,"byteStride":24,"target":34962},{"buffer":0,"byteLength":24,"byteOffset":76,"target":34962}],"buffers":[{"byteLength":100,"uri":"data:application/octet-stream;base64,AAECAAAAwD8AAIC/AAAAv83MzD3NzEw+mpmZPgAAAL8AACBAAABAP83MzD4AAAA/mpkZPwAAAMAAAIA/mpmZPjMzMz/NzEw/ZmZmP5qZmT4AAAAAAAAAAAAAAD+amZk+mpmZPg=="}]}
{"asset":{"version":"2.0"},"meshes":[{"name":"Non-indexed mesh","primitives":[{"attributes":{"POSITION":1,"TEXCOORD_0":3}}]},{"name":"Indexed mesh","primitives":[{"attributes":{"NORMAL":2,"POSITION":1},"indices":0}]},{"name":"Attribute-less mesh","primitives":[{"attributes":{}}]},{"name":"Attribute-less indexed mesh","primitives":[{"attributes":{},"indices":0}]}],"accessors":[{"bufferView":0,"componentType":5121,"count":3,"type":"SCALAR"},{"bufferView":1,"componentType":5126,"count":3,"type":"VEC3"},{"bufferView":1,"byteOffset":12,"componentType":5126,"count":3,"type":"VEC3"},{"bufferView":2,"componentType":5126,"count":3,"type":"VEC2"}],"bufferViews":[{"buffer":0,"byteLength":3,"byteOffset":0,"target":34963},{"buffer":0,"byteLength":72,"byteOffset":4,"byteStride":24,"target":34962},{"buffer":0,"byteLength":24,"byteOffset":76,"target":34962}],"buffers":[{"byteLength":100,"uri":"data:application/octet-stream;base64,AAECAAAAwD8AAIC/AAAAv83MzD3NzEw+mpmZPgAAAL8AACBAAABAP83MzD4AAAA/mpkZPwAAAMAAAIA/mpmZPjMzMz/NzEw/ZmZmP5qZmT4AAAAAAAAAAAAAAD+amZk+mpmZPg=="}]}
40 changes: 38 additions & 2 deletions src/MagnumPlugins/TinyGltfImporter/Test/mesh-invalid.gltf
Original file line number Diff line number Diff line change
Expand Up @@ -187,13 +187,33 @@
}
]
},
{
"name": "normalized float",
"primitives": [
{
"attributes": {
"_THING": 15
}
}
]
},
{
"name": "non-normalized byte matrix",
"primitives": [
{
"attributes": {
"_THING": 16
}
}
]
},
{
"name": "accessor index out of bounds",
"primitives": [
{
"note": "for indices tinygltf handles this, for attributes not. HEH.",
"note": "for indices tinygltf handles this, for attributes not. HEH. Using TEXCOORD so the initial check for texture coordinate types is verified for bounds checks also.",
"attributes": {
"POSITION": 15
"TEXCOORD_1": 17
}
}
]
Expand Down Expand Up @@ -308,6 +328,22 @@
"componentType": 5126,
"count": 9,
"type": "VEC3"
},
{
"name": "15",
"bufferView": 0,
"componentType": 5130,
"normalized": true,
"count": 1,
"type": "SCALAR"
},
{
"name": "16",
"bufferView": 1,
"componentType": 5120,
"normalized": false,
"count": 1,
"type": "MAT2"
}
],
"bufferViews": [
Expand Down
Binary file modified src/MagnumPlugins/TinyGltfImporter/Test/mesh.glb
Binary file not shown.
Loading

0 comments on commit 79910b4

Please sign in to comment.