Skip to content

Commit

Permalink
StanfordImporter: implement support for per-face/per-vertex object IDs.
Browse files Browse the repository at this point in the history
  • Loading branch information
mosra committed Mar 30, 2020
1 parent 4337abe commit 68e4fa5
Show file tree
Hide file tree
Showing 18 changed files with 236 additions and 78 deletions.
6 changes: 3 additions & 3 deletions doc/changelog-plugins.dox
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ namespace Magnum {
and [mosra/magnum-plugins#81](https://github.com/mosra/magnum-plugins/pull/81))
- Tangent and bitangent import in @ref Trade::AssimpImporter "AssimpImporter",
normal texture support
- Vertex color, normal and texture coordinate import in
@ref Trade::StanfordImporter "StanfordImporter", colors and normals are
supported per-face as well
- Vertex color, normal, texture coordinate and per-vertex object ID import in
@ref Trade::StanfordImporter "StanfordImporter"; colors, normals and object
IDs are supported per-face as well
- Custom vertex and face attribute import in
@ref Trade::StanfordImporter "StanfordImporter"
- Support for the `KHR_lights_punctual` extension in
Expand Down
4 changes: 4 additions & 0 deletions src/MagnumPlugins/StanfordImporter/StanfordImporter.conf
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@ perFaceToPerVertex=true
# checks and thus might cause broken files to not be detected correctly in rare
# cases.
triangleFastPath=true

# The non-standard MeshAttribute::ObjectId is by default recognized under this
# name. Change if your file uses a different identifier.
objectIdAttribute=object_id
# [config]
37 changes: 37 additions & 0 deletions src/MagnumPlugins/StanfordImporter/StanfordImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ StanfordImporter::StanfordImporter() {
/** @todo horrible workaround, fix this properly */
configuration().setValue("perFaceToPerVertex", true);
configuration().setValue("triangleFastPath", true);
configuration().setValue("objectIdAttribute", "object_id");
}

StanfordImporter::StanfordImporter(PluginManager::AbstractManager& manager, const std::string& plugin): AbstractImporter{manager, plugin} {}
Expand Down Expand Up @@ -242,12 +243,15 @@ void StanfordImporter::doOpenData(Containers::ArrayView<const char> data) {
Math::Vector3<VertexFormat> normalFormats;
Math::Vector2<VertexFormat> textureCoordinateFormats;
Math::Vector4<VertexFormat> colorFormats;
VertexFormat objectIdFormat{};
Vector3ui positionOffsets{~UnsignedInt{}};
Vector3ui normalOffsets{~UnsignedInt{}};
Vector2ui textureCoordinateOffsets{~UnsignedInt{}};
Vector4ui colorOffsets{~UnsignedInt{}};
UnsignedInt objectIdOffset = ~UnsignedInt{};
bool perFaceNormals = false;
bool perFaceColors = false;
bool perFaceObjectIds = false;
{
std::size_t vertexComponentOffset{};
PropertyType propertyType{};
Expand Down Expand Up @@ -334,6 +338,9 @@ void StanfordImporter::doOpenData(Containers::ArrayView<const char> data) {
} else if(tokens[2] == "alpha") {
colorOffsets.w() = vertexComponentOffset;
colorFormats.w() = componentFormat;
} else if(tokens[2] == configuration().value("objectIdAttribute")) {
objectIdOffset = vertexComponentOffset;
objectIdFormat = componentFormat;

/* Unknown component, add to the attribute list. Stride is
not known yet, using 0 until it's updated later. */
Expand Down Expand Up @@ -412,6 +419,10 @@ void StanfordImporter::doOpenData(Containers::ArrayView<const char> data) {
} else if(tokens[2] == "alpha") {
colorOffsets.w() = faceComponentOffset;
colorFormats.w() = componentFormat;
} else if(tokens[2] == configuration().value("objectIdAttribute")) {
perFaceObjectIds = true;
objectIdOffset = faceComponentOffset;
objectIdFormat = componentFormat;

/* Unknown component, add to the face attribute list.
Stride and actual triangle face count is not known yet,
Expand Down Expand Up @@ -586,6 +597,32 @@ void StanfordImporter::doOpenData(Containers::ArrayView<const char> data) {
colorOffsets.x(), 0u, std::ptrdiff_t(state->faceIndicesOffset + state->faceSkip));
}

/* Wrap up object IDs, if any */
if(objectIdOffset < ~UnsignedInt{}) {
/* Same as with indices, various datasets in the wild use signed
integers. Interpret them as unsigned. */
VertexFormat format;
if(objectIdFormat == VertexFormat::UnsignedInt || objectIdFormat == VertexFormat::Int)
format = VertexFormat::UnsignedInt;
else if(objectIdFormat == VertexFormat::UnsignedShort || objectIdFormat == VertexFormat::Short)
format = VertexFormat::UnsignedShort;
else if(objectIdFormat == VertexFormat::UnsignedByte || objectIdFormat == VertexFormat::Byte)
format = VertexFormat::UnsignedByte;
else {
Error{} << "Trade::StanfordImporter::openData(): unsupported object ID type" << objectIdFormat;
return;
}

/* Add the attribute. If it is per-face, actual triangle face count is
not known yet, using 0 until after all faces are parsed. */
if(!perFaceObjectIds) arrayAppend(state->attributeData,
Containers::InPlaceInit, MeshAttribute::ObjectId, format,
objectIdOffset, state->vertexCount, std::ptrdiff_t(state->vertexStride));
else arrayAppend(state->faceAttributeData,
Containers::InPlaceInit, MeshAttribute::ObjectId, format,
objectIdOffset, 0u, std::ptrdiff_t(state->faceIndicesOffset + state->faceSkip));
}

if(data.size() < state->vertexStride*state->vertexCount) {
Error{} << "Trade::StanfordImporter::openData(): incomplete vertex data";
return;
Expand Down
13 changes: 11 additions & 2 deletions src/MagnumPlugins/StanfordImporter/StanfordImporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,14 @@ of PLY features, which however shouldn't affect any real-world models.
or @ref VertexFormat::Vector3usNormalized /
@ref VertexFormat::Vector4usNormalized. Signed, 32-bit integer and double
colors are not supported.
- Per-vertex object ID attribute is imported as either
@ref VertexFormat::UnsignedInt, @ref VertexFormat::UnsignedShort or
@ref VertexFormat::UnsignedByte. By default `object_id` is the recognized
name, use the @cb{.ini} objectIdAttribute @ce
@ref Trade-StanfordImporter-configuration "configuration option" to change
the identifier that's being looked for. Because there are real-world files
with signed object IDs, signed types are allowed as well, but interpreted
as unsigned.
- Indices are imported as either @ref MeshIndexType::UnsignedByte,
@ref MeshIndexType::UnsignedShort or @ref MeshIndexType::UnsignedInt. Quads
are triangulated, but higher-order polygons are not supported. Because
Expand Down Expand Up @@ -158,8 +166,9 @@ instance with @ref MeshPrimitive::Faces. The faces are triangulated, which
means each item contains data for three vertices in the
@ref MeshPrimitive::Triangles mesh at <tt>level</tt> @cpp 0 @ce.
From the builtin attributes, colors and normals can be either per-vertex or
per-face, positions and texture coordinates are always per-vertex.
From the builtin attributes, colors, normals and object IDs can be either
per-vertex or per-face, positions and texture coordinates are always
per-vertex.
@subsection Trade-StanfordImporter-behavior-custom-attributes Custom attributes
Expand Down
11 changes: 6 additions & 5 deletions src/MagnumPlugins/StanfordImporter/Test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,18 @@ corrade_add_test(StanfordImporterTest StanfordImporterTest.cpp
normals-not-same-type.ply
normals-not-tightly-packed.ply
normals-unsupported-type.ply
objectid-unsupported-type.ply
per-face-colors-be.ply
per-face-normals.ply
positions-colors-normals-texcoords-float-indices-int-be.ply
positions-colors-normals-texcoords-float-indices-int.ply
per-face-normals-objectid.ply
positions-colors-normals-texcoords-float-objectid-uint-indices-int-be.ply
positions-colors-normals-texcoords-float-objectid-uint-indices-int.ply
positions-colors4-normals-texcoords-float-indices-int-be-unaligned.ply
positions-colors4-float-indices-int.ply
positions-float-indices-uint.ply
positions-char-colors4-ushort-texcoords-uchar-indices-short-be.ply
positions-short-colors-uchar-texcoords-ushort-indices-char.ply
positions-uchar-normals-char-indices-ushort.ply
positions-ushort-normals-short-indices-uchar-be.ply
positions-uchar-normals-char-objectid-short-indices-ushort.ply
positions-ushort-normals-short-objectid-uchar-indices-uchar-be.ply
positions-not-same-type.ply
positions-not-tightly-packed.ply
positions-unsupported-type.ply
Expand Down
89 changes: 69 additions & 20 deletions src/MagnumPlugins/StanfordImporter/Test/StanfordImporterTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ constexpr struct {
{"colors4-not-tightly-packed", "expecting color components to be tightly packed, but got offsets Vector(13, 14, 15, 12) for a 1-byte type", true},
{"colors-unsupported-type", "unsupported color component type VertexFormat::Int", true},

{"objectid-unsupported-type", "unsupported object ID type VertexFormat::Float", true},

{"unsupported-face-size", "unsupported face size 5", false}
};

Expand All @@ -130,69 +132,83 @@ constexpr struct {
constexpr struct {
const char* filename;
MeshIndexType indexType;
VertexFormat positionFormat, colorFormat, normalFormat, texcoordFormat;
VertexFormat positionFormat, colorFormat, normalFormat, texcoordFormat,
objectIdFormat;
const char* objectIdAttribute;
UnsignedInt attributeCount, faceAttributeCount;
} ParseData[]{
/* GCC 4.8 doesn't like just {}, needs VertexFormat{} */
{"positions-float-indices-uint", MeshIndexType::UnsignedInt,
VertexFormat::Vector3, VertexFormat{},
VertexFormat{}, VertexFormat{},
1, 0},
VertexFormat{}, nullptr, 1, 0},
/* All supported attributes, in the canonical type */
{"positions-colors-normals-texcoords-float-indices-int",
{"positions-colors-normals-texcoords-float-objectid-uint-indices-int",
MeshIndexType::UnsignedInt,
VertexFormat::Vector3, VertexFormat::Vector3,
VertexFormat::Vector3, VertexFormat::Vector2, 4, 0},
VertexFormat::Vector3, VertexFormat::Vector2,
VertexFormat::UnsignedInt, nullptr, 5, 0},
/* Four-component colors */
{"positions-colors4-float-indices-int", MeshIndexType::UnsignedInt,
VertexFormat::Vector3, VertexFormat::Vector4,
VertexFormat{}, VertexFormat{}, 2, 0},
VertexFormat{}, VertexFormat{},
VertexFormat{}, nullptr, 2, 0},
/* Testing endian flip */
{"positions-colors-normals-texcoords-float-indices-int-be",
{"positions-colors-normals-texcoords-float-objectid-uint-indices-int-be",
MeshIndexType::UnsignedInt,
VertexFormat::Vector3, VertexFormat::Vector3,
VertexFormat::Vector3, VertexFormat::Vector2, 4, 0},
VertexFormat::Vector3, VertexFormat::Vector2,
VertexFormat::UnsignedInt, nullptr, 5, 0},
/* Testing endian flip of unaligned data */
{"positions-colors4-normals-texcoords-float-indices-int-be-unaligned",
MeshIndexType::UnsignedInt,
VertexFormat::Vector3, VertexFormat::Vector4,
VertexFormat::Vector3, VertexFormat::Vector2, 8, 1},
VertexFormat::Vector3, VertexFormat::Vector2,
VertexFormat{}, nullptr, 8, 1},
/* Testing various packing variants (hopefully exhausting all combinations) */
{"positions-uchar-normals-char-indices-ushort",
{"positions-uchar-normals-char-objectid-short-indices-ushort",
MeshIndexType::UnsignedShort,
VertexFormat::Vector3ub, VertexFormat{},
VertexFormat::Vector3bNormalized, VertexFormat{}, 2, 0},
VertexFormat::Vector3bNormalized, VertexFormat{},
VertexFormat::UnsignedShort, "OBJECTID", 3, 0},
{"positions-char-colors4-ushort-texcoords-uchar-indices-short-be",
MeshIndexType::UnsignedShort,
VertexFormat::Vector3b, VertexFormat::Vector4usNormalized,
VertexFormat{}, VertexFormat::Vector2ubNormalized, 3, 0},
{"positions-ushort-normals-short-indices-uchar-be",
VertexFormat{}, VertexFormat::Vector2ubNormalized,
VertexFormat{}, nullptr, 3, 0},
{"positions-ushort-normals-short-objectid-uchar-indices-uchar-be",
MeshIndexType::UnsignedByte,
VertexFormat::Vector3us, VertexFormat{},
VertexFormat::Vector3sNormalized, VertexFormat{}, 2, 0},
VertexFormat::Vector3sNormalized, VertexFormat{},
VertexFormat::UnsignedByte, "semantic", 3, 0},
{"positions-short-colors-uchar-texcoords-ushort-indices-char",
MeshIndexType::UnsignedByte,
VertexFormat::Vector3s, VertexFormat::Vector3ubNormalized,
VertexFormat{}, VertexFormat::Vector2usNormalized, 3, 0},
VertexFormat{}, VertexFormat::Vector2usNormalized,
VertexFormat{}, nullptr, 3, 0},
/* CR/LF instead of LF */
{"crlf", MeshIndexType::UnsignedByte,
VertexFormat::Vector3us, VertexFormat{},
VertexFormat{}, VertexFormat{}, 1, 0}
VertexFormat{}, VertexFormat{},
VertexFormat{}, nullptr, 1, 0}
};

constexpr struct {
const char* name;
const char* filename;
UnsignedInt faceAttributeCount;
MeshIndexType indexType;
VertexFormat colorFormat, normalFormat;
VertexFormat colorFormat, normalFormat, objectIdFormat;
} ParsePerFaceData[]{
{"per-face nothing", "positions-float-indices-uint.ply", 0,
MeshIndexType::UnsignedInt, VertexFormat{}, VertexFormat{}},
MeshIndexType::UnsignedInt,
VertexFormat{}, VertexFormat{}, VertexFormat{}},
{"per-face colors", "per-face-colors-be.ply", 1,
MeshIndexType::UnsignedShort, VertexFormat::Vector4, VertexFormat{}},
{"per-face normals", "per-face-normals.ply", 1,
MeshIndexType::UnsignedByte, VertexFormat{}, VertexFormat::Vector3}
MeshIndexType::UnsignedShort,
VertexFormat::Vector4, VertexFormat{}, VertexFormat{}},
{"per-face normals, object ids", "per-face-normals-objectid.ply", 2,
MeshIndexType::UnsignedByte,
VertexFormat{}, VertexFormat::Vector3, VertexFormat::UnsignedShort}
};

constexpr struct {
Expand Down Expand Up @@ -363,6 +379,9 @@ constexpr Vector2 TextureCoordinates[]{
{0.466666f, 0.333333f},
{0.866666f, 0.066666f}
};
constexpr UnsignedInt ObjectIds[]{
215, 71, 133, 5, 196
};

void StanfordImporterTest::parse() {
auto&& data = ParseData[testCaseInstanceId()];
Expand All @@ -373,6 +392,8 @@ void StanfordImporterTest::parse() {
/* Disable per-face to per-vertex conversion, that's tested thoroughly in
parsePerFace() */
importer->configuration().setValue("perFaceToPerVertex", false);
if(data.objectIdAttribute)
importer->configuration().setValue("objectIdAttribute", data.objectIdAttribute);

CORRADE_VERIFY(importer->openFile(Utility::Directory::join(STANFORDIMPORTER_TEST_DIR, Utility::formatString("{}.ply", data.filename))));
CORRADE_COMPARE(importer->meshCount(), 1);
Expand Down Expand Up @@ -435,6 +456,14 @@ void StanfordImporterTest::parse() {
Containers::stridedArrayView(TextureCoordinates),
TestSuite::Compare::Container);
}

if(data.objectIdFormat != VertexFormat{}) {
CORRADE_VERIFY(mesh->hasAttribute(MeshAttribute::ObjectId));
CORRADE_COMPARE(mesh->attributeFormat(MeshAttribute::ObjectId), data.objectIdFormat);
CORRADE_COMPARE_AS(mesh->objectIdsAsArray(),
Containers::stridedArrayView(ObjectIds),
TestSuite::Compare::Container);
}
}

void StanfordImporterTest::parsePerFace() {
Expand All @@ -444,6 +473,7 @@ void StanfordImporterTest::parsePerFace() {
Containers::Pointer<AbstractImporter> importer = _manager.instantiate("StanfordImporter");

importer->configuration().setValue("perFaceToPerVertex", false);
importer->configuration().setValue("objectIdAttribute", "objectid");

CORRADE_VERIFY(importer->openFile(Utility::Directory::join(STANFORDIMPORTER_TEST_DIR, data.filename)));
CORRADE_COMPARE(importer->meshCount(), 1);
Expand Down Expand Up @@ -493,6 +523,14 @@ void StanfordImporterTest::parsePerFace() {
{-0.0f, -0.133333f, -1.0f}
}), TestSuite::Compare::Container);
}

if(data.objectIdFormat != VertexFormat{}) {
CORRADE_VERIFY(faceMesh->hasAttribute(MeshAttribute::ObjectId));
CORRADE_COMPARE(faceMesh->attributeFormat(MeshAttribute::ObjectId), data.objectIdFormat);
CORRADE_COMPARE_AS(faceMesh->objectIdsAsArray(),
Containers::arrayView<UnsignedInt>({117, 117, 56}),
TestSuite::Compare::Container);
}
}

void StanfordImporterTest::parsePerFaceToPerVertex() {
Expand All @@ -503,6 +541,7 @@ void StanfordImporterTest::parsePerFaceToPerVertex() {

/* Done by default */
//importer->configuration().setValue("perFaceToPerVertex", true);
importer->configuration().setValue("objectIdAttribute", "objectid");

CORRADE_VERIFY(importer->openFile(Utility::Directory::join(STANFORDIMPORTER_TEST_DIR, data.filename)));
CORRADE_COMPARE(importer->meshCount(), 1);
Expand Down Expand Up @@ -571,6 +610,16 @@ void StanfordImporterTest::parsePerFaceToPerVertex() {
{-0.0f, -0.133333f, -1.0f}
}), TestSuite::Compare::Container);
}

if(data.objectIdFormat != VertexFormat{}) {
CORRADE_VERIFY(mesh->hasAttribute(MeshAttribute::ObjectId));
CORRADE_COMPARE(mesh->attributeFormat(MeshAttribute::ObjectId), data.objectIdFormat);
CORRADE_COMPARE_AS(mesh->objectIdsAsArray(),
Containers::arrayView<UnsignedInt>({
117, 117, 117, 117,
56, 56, 56}),
TestSuite::Compare::Container);
}
}

void StanfordImporterTest::empty() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
ply
format binary_little_endian 1.0
element vertex 0
property float x
property float y
property float z
property float object_id
element face 0
property list char char vertex_indices
end_header
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ property float nx
property float ny
property float nz
property list int32 uchar vertex_indices
property ushort objectid
"""
type = '<3f 3f 3f 3f 3f 3fi4B3fi3B'
type = '<3f 3f 3f 3f 3f 3fi4BH 3fi3BH'
input = [
1.0, 3.0, 2.0,
1.0, 1.0, 2.0,
Expand All @@ -21,10 +22,12 @@ input = [
-0.66666666666666667,
-0.93333333333333333,
4, 0, 1, 2, 3,
117,
-0.0,
-0.13333333333333333,
-1.0,
3, 3, 2, 4,
56
]

# kate: hl python
Loading

0 comments on commit 68e4fa5

Please sign in to comment.