Browse Source

ObjImporter: support quads.

Now I'm able to measure -- with the Rungholt scene from McGuire
archives, it takes it a whooping 7.3 seconds, measured with

    magnum-sceneconverter --info-meshes --profile rungholt.obj

Compared to that, AssimpImporter in the default configuration takes 10.3
seconds, but with

    magnum-sceneconverter --info-meshes --profile -I AssimpImporter \
      -i postprocess/JoinIdenticalVertices=false,postprocess/SortByPType=false \
      rungholt.obj

it takes just 4.3 seconds. And has the same vertex data size in total,
so that should probably be the default. I got some work to do, then.
master
Vladimír Vondruš 4 years ago
parent
commit
e1e141abf2
  1. 2
      doc/changelog.dox
  2. 57
      src/MagnumPlugins/ObjImporter/ObjImporter.cpp
  3. 3
      src/MagnumPlugins/ObjImporter/ObjImporter.h
  4. 1
      src/MagnumPlugins/ObjImporter/Test/CMakeLists.txt
  5. 63
      src/MagnumPlugins/ObjImporter/Test/ObjImporterTest.cpp
  6. 4
      src/MagnumPlugins/ObjImporter/Test/invalid-number-count.obj
  7. 31
      src/MagnumPlugins/ObjImporter/Test/mesh-quads.obj

2
doc/changelog.dox

@ -1097,7 +1097,7 @@ See also:
consistent with @ref Trade::SceneData::findFieldId().
- Added @ref Trade::MeshData::attributeId() that returns ID of an attribute
in a set of attributes of the same name
- @relativeref{Trade,ObjImporter} now supports negative indices
- @relativeref{Trade,ObjImporter} now supports quads and negative indices
- Added @ref Trade::TextureType::Texture1DArray,
@relativeref{Trade::TextureType,Texture2DArray} and
@relativeref{Trade::TextureType,CubeMapArray} in order to be able to

57
src/MagnumPlugins/ObjImporter/ObjImporter.cpp

@ -383,18 +383,19 @@ Containers::Optional<MeshData> ObjImporter::doMesh(const UnsignedInt id, Unsigne
/* Indices */
} else if(keyword == "p"_s || keyword == "l"_s || keyword == "f"_s) {
/* Decide on how many tuples we expect */
std::size_t indexTupleCount;
if(keyword == "p"_s) indexTupleCount = 1;
else if(keyword == "l"_s) indexTupleCount = 2;
else if(keyword == "f"_s) indexTupleCount = 3;
/* Decide on how many tuples we expect. Since we handle both
triangles and quads, it can't be an exact count. */
std::size_t maxIndexTupleCount;
if(keyword == "p"_s) maxIndexTupleCount = 1;
else if(keyword == "l"_s) maxIndexTupleCount = 2;
else if(keyword == "f"_s) maxIndexTupleCount = 4;
else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
/* Parse them all. If there's less than expected, `i` would be too
small; if there's more then `contents` would stay non-empty. */
Vector3ui data[3];
Vector3ui data[4];
std::size_t i = 0;
for(; i != indexTupleCount && contents; ++i) {
for(; i != maxIndexTupleCount && contents; ++i) {
const Containers::StringView foundSpace = contents.findAnyOr(Whitespace, contents.end());
Containers::StringView indexTuple = contents.prefix(foundSpace.begin());
@ -457,6 +458,9 @@ Containers::Optional<MeshData> ObjImporter::doMesh(const UnsignedInt id, Unsigne
primitive = MeshPrimitive::Points;
/** @todo fix arrayAppend() to not need the cast here */
arrayAppend(indices, Containers::ArrayView<const Vector3ui>{data}.prefix(1));
/* Lines */
} else if(keyword == "l") {
if(primitive && primitive != MeshPrimitive::Lines) {
@ -470,6 +474,9 @@ Containers::Optional<MeshData> ObjImporter::doMesh(const UnsignedInt id, Unsigne
primitive = MeshPrimitive::Lines;
/** @todo fix arrayAppend() to not need the cast here */
arrayAppend(indices, Containers::ArrayView<const Vector3ui>{data}.prefix(2));
/* Faces */
} else if(keyword == "f") {
if(primitive && primitive != MeshPrimitive::Triangles) {
@ -477,17 +484,45 @@ Containers::Optional<MeshData> ObjImporter::doMesh(const UnsignedInt id, Unsigne
return Containers::NullOpt;
}
if(i < 3 || contents) {
Error() << "Trade::ObjImporter::mesh(): expected exactly 3 position index tuples for a triangle, got" << line.suffix(keywordEnd.end());
Error() << "Trade::ObjImporter::mesh(): expected 3 or 4 position index tuples for a face, got" << line.suffix(keywordEnd.end());
return Containers::NullOpt;
}
/* If it's a quad, convert it to two triangles */
if(i == 4) {
/** @todo use MeshTools::generateQuadIndices() once it
can take extra index data into account */
/* 0 0---3
|\ \ |
| \ \ |
| \ \|
1---2 2 */
arrayAppend(indices, {
data[0],
data[1],
data[2],
data[0],
data[2],
data[3]
});
/* If we have texture coordinate / normal indices, add two
more to the counters as well. If they matched the index
array size before, they'll continue to match; if they
didn't, they'll continue to not match. */
if(textureCoordinateIndexCount)
textureCoordinateIndexCount += 2;
if(normalIndexCount)
normalIndexCount += 2;
} else {
/** @todo fix arrayAppend() to not need the cast here */
arrayAppend(indices, Containers::ArrayView<const Vector3ui>{data}.prefix(3));
}
primitive = MeshPrimitive::Triangles;
} else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
/** @todo fix arrayAppend() to not need the cast here */
arrayAppend(indices, Containers::ArrayView<const Vector3ui>{data}.prefix(i));
/* Unknown keyword */
} else {
Error{} << "Trade::ObjImporter::mesh(): unknown keyword" << keyword;

3
src/MagnumPlugins/ObjImporter/ObjImporter.h

@ -108,9 +108,10 @@ positions with optional @ref VertexFormat::Vector3 normals and
Negative indices (where @cpp -1 @ce is the last position / texture coordinate
/ normal known at given point in the file, @cpp -2 @ce is the second-to-last,
etc.), produced for example by 3ds Max or [Mineways](http://mineways.com), are
supported. Quads are converted to two triangles, higher-order polygons are not
supported.
Polygons (quads etc.) and material properties are currently not supported.
Material properties are currently not supported.
*/
class MAGNUM_OBJIMPORTER_EXPORT ObjImporter: public AbstractImporter {
public:

1
src/MagnumPlugins/ObjImporter/Test/CMakeLists.txt

@ -66,6 +66,7 @@ corrade_add_test(ObjImporterTest ObjImporterTest.cpp
mesh-primitive-lines.obj
mesh-primitive-points.obj
mesh-primitive-triangles.obj
mesh-quads.obj
mesh-texture-coordinates.obj
mesh-texture-coordinates-normals.obj
mesh-texture-coordinates-optional-coordinate.obj)

63
src/MagnumPlugins/ObjImporter/Test/ObjImporterTest.cpp

@ -57,6 +57,7 @@ struct ObjImporterTest: TestSuite::Tester {
void meshTextureCoordinatesNormals();
void meshNegativeIndices();
void meshQuads();
void meshIgnoredKeyword();
@ -153,8 +154,8 @@ const struct {
{"four-component index tuple", "invalid integer literal 1/1"},
{"point with two indices", "expected exactly 1 position index tuple for a point, got 9 9"},
{"line with one index", "expected exactly 2 position index tuples for a line, got 10"},
{"triangle with two indices", "expected exactly 3 position index tuples for a triangle, got 11 11"},
{"quad", "expected exactly 3 position index tuples for a triangle, got 12 12 12 12"}
{"triangle with two indices", "expected 3 or 4 position index tuples for a face, got 11 11"},
{"five-vertex face", "expected 3 or 4 position index tuples for a face, got 12 12 12 12 12"}
};
const struct {
@ -199,6 +200,7 @@ ObjImporterTest::ObjImporterTest() {
&ObjImporterTest::meshTextureCoordinatesNormals,
&ObjImporterTest::meshNegativeIndices,
&ObjImporterTest::meshQuads,
&ObjImporterTest::meshIgnoredKeyword,
@ -509,6 +511,63 @@ void ObjImporterTest::meshNegativeIndices() {
}), TestSuite::Compare::Container);
}
void ObjImporterTest::meshQuads() {
Containers::Pointer<AbstractImporter> importer = _manager.instantiate("ObjImporter");
CORRADE_VERIFY(importer->openFile(Utility::Path::join(OBJIMPORTER_TEST_DIR, "mesh-quads.obj")));
CORRADE_COMPARE(importer->meshCount(), 1);
/* 1 1
/ \ / \
/ \ / \
2 --- 5 2 --- 3
| | -> 4 --- 7
| | | |
3 --- 4 5 --- 6 */
const Containers::Optional<MeshData> data = importer->mesh(0);
CORRADE_VERIFY(data);
CORRADE_COMPARE(data->primitive(), MeshPrimitive::Triangles);
CORRADE_COMPARE(data->attributeCount(), 3);
CORRADE_COMPARE_AS(data->attribute<Vector3>(MeshAttribute::Position),
Containers::arrayView<Vector3>({
{0.0f, 3.0f, 0.0f},
{-1.0f, 1.0f, 0.0f},
{1.0f, 1.0f, 0.0f},
{-1.0f, 1.0f, 0.0f},
{-1.0f, -1.0f, 0.0f},
{1.0f, -1.0f, 0.0f},
{1.0f, 1.0f, 0.0f},
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(data->attribute<Vector2>(MeshAttribute::TextureCoordinates),
Containers::arrayView<Vector2>({
{0.5f, 1.0f},
{0.0f, 0.5f},
{1.0f, 0.5f},
{0.0f, 0.5f},
{0.0f, 0.0f},
{1.0f, 0.0f},
{1.0f, 0.5f},
}), TestSuite::Compare::Container);
CORRADE_COMPARE_AS(data->attribute<Vector3>(MeshAttribute::Normal),
Containers::arrayView<Vector3>({
{0.0f, 1.0f, 0.0f},
{-1.0f, 0.0f, 0.0f},
{1.0f, 0.0f, 0.0f},
{0.0f, -1.0f, 0.0f},
{0.0f, -1.0f, 0.0f},
{0.0f, -1.0f, 0.0f},
{0.0f, -1.0f, 0.0f},
}), TestSuite::Compare::Container);
CORRADE_VERIFY(data->isIndexed());
CORRADE_COMPARE(data->indexType(), MeshIndexType::UnsignedInt);
CORRADE_COMPARE_AS(data->indices<UnsignedInt>(),
Containers::arrayView<UnsignedInt>({
0, 1, 2, 3, 4, 5, 3, 5, 6
}), TestSuite::Compare::Container);
}
void ObjImporterTest::meshIgnoredKeyword() {
Containers::Pointer<AbstractImporter> importer = _manager.instantiate("ObjImporter");
CORRADE_VERIFY(importer->openFile(Utility::Path::join(OBJIMPORTER_TEST_DIR, "mesh-ignored-keyword.obj")));

4
src/MagnumPlugins/ObjImporter/Test/invalid-number-count.obj

@ -57,6 +57,6 @@ o triangle with two indices
v 1 2 3
f 11 11
o quad
o five-vertex face
v 1 2 3
f 12 12 12 12
f 12 12 12 12 12

31
src/MagnumPlugins/ObjImporter/Test/mesh-quads.obj

@ -0,0 +1,31 @@
# A house.
#
# 1
# / \
# / \
# 2 --- 5
# | |
# | |
# 3 --- 4
v 0 3 0
v -1 1 0
v -1 -1 0
v 1 -1 0
v 1 1 0
vt 0.5 1
vt 0 0.5
vt 0 0
vt 1 0
vt 1 0.5
# Normals of the roof are pointing up, to the left and to the right
vn 0 1 0
vn -1 0 0
vn 1 0 0
f 1/1/1 2/2/2 5/5/3
# Normals of the walls are all pointing down
vn 0 -1 0
f 2/2/4 3/3/4 4/4/4 5/5/4
Loading…
Cancel
Save