Browse Source

sceneconverter: rethink how reference count is calculated.

Because this tool is often useful to debug broken importers, it's not
wise to parse scenes even if just --info-materials was specified to
gather material reference count. The new behavior is that reference
count will be listed only if both the referer and referee data type info
is specified.

This also allowed me to remove the redundant `references` field from
Info structures -- now just the arrays are used, and if the array is
empty, then it covers both the case when refcounts were not calculated
(in which case the displayed info would be wrong) and when there's no
refered data at all (in which case the reference count info would be
superfluous).

There was also an issue that texture reference count was not calculated
when --info-materials was not set, this is now the desired behavior
(except that it's not printing the invalid zero count at all).
pull/542/merge
Vladimír Vondruš 4 years ago
parent
commit
f10d74041b
  1. 234
      src/Magnum/MeshTools/sceneconverter.cpp

234
src/Magnum/MeshTools/sceneconverter.cpp

@ -128,6 +128,13 @@ Arguments:
- `-v`, `--verbose` --- verbose output from importer and converter plugins - `-v`, `--verbose` --- verbose output from importer and converter plugins
- `--profile` --- measure import and conversion time - `--profile` --- measure import and conversion time
If any of the `--info-*` options are given, the utility will print information
about given data present in the file. In this case no conversion is done and
output file doesn't need to be specified. In case one data references another
and both `--info-*` options are specified, the output will also list reference
count (for example, `--info-scenes` together with `--info-meshes` will print
how many objects reference given mesh).
The `-i` / `--importer-options` and `-c` / `--converter-options` arguments The `-i` / `--importer-options` and `-c` / `--converter-options` arguments
accept a comma-separated list of key/value pairs to set in the importer / accept a comma-separated list of key/value pairs to set in the importer /
converter plugin configuration. If the `=` character is omitted, it's converter plugin configuration. If the `=` character is omitted, it's
@ -262,6 +269,13 @@ int main(int argc, char** argv) {
}) })
.setGlobalHelp(R"(Converts scenes of different formats. .setGlobalHelp(R"(Converts scenes of different formats.
If any of the --info-* options are given, the utility will print information
about given data present in the file. In this case no conversion is done and
output file doesn't need to be specified. In case one data references another
and both --info-* options are specified, the output will also list reference
count (for example, --info-scenes together with --info-meshes will print how
many objects reference given mesh).
The -i / --importer-options and -c / --converter-options arguments accept a The -i / --importer-options and -c / --converter-options arguments accept a
comma-separated list of key/value pairs to set in the importer / converter comma-separated list of key/value pairs to set in the importer / converter
plugin configuration. If the = character is omitted, it's equivalent to saying plugin configuration. If the = character is omitted, it's equivalent to saying
@ -330,28 +344,24 @@ used.)")
struct SkinInfo { struct SkinInfo {
UnsignedInt skin; UnsignedInt skin;
UnsignedInt references;
Trade::SkinData3D data{{}, {}}; Trade::SkinData3D data{{}, {}};
std::string name; std::string name;
}; };
struct LightInfo { struct LightInfo {
UnsignedInt light; UnsignedInt light;
UnsignedInt references;
Trade::LightData data{{}, {}, {}}; Trade::LightData data{{}, {}, {}};
std::string name; std::string name;
}; };
struct MaterialInfo { struct MaterialInfo {
UnsignedInt material; UnsignedInt material;
UnsignedInt references;
Trade::MaterialData data{{}, {}}; Trade::MaterialData data{{}, {}};
std::string name; std::string name;
}; };
struct TextureInfo { struct TextureInfo {
UnsignedInt texture; UnsignedInt texture;
UnsignedInt references;
Trade::TextureData data{{}, {}, {}, {}, {}, {}}; Trade::TextureData data{{}, {}, {}, {}, {}, {}};
std::string name; std::string name;
}; };
@ -367,7 +377,6 @@ used.)")
struct MeshInfo { struct MeshInfo {
UnsignedInt mesh, level; UnsignedInt mesh, level;
UnsignedInt references;
MeshPrimitive primitive; MeshPrimitive primitive;
UnsignedInt indexCount, vertexCount; UnsignedInt indexCount, vertexCount;
MeshIndexType indexType; MeshIndexType indexType;
@ -398,59 +407,63 @@ used.)")
/* Parse everything first to avoid errors interleaved with output */ /* Parse everything first to avoid errors interleaved with output */
/* Scene properties, together with counting how much is each mesh / /* Scene properties, together with counting how much is each mesh /
light / material / skin shared. Texture reference count is light / material / skin shared (which gets used only if both
calculated when parsing materials. */ --info-scenes and --info-{lights,materials,skins} is passed).
Texture reference count is calculated when parsing materials. */
Containers::Array<SceneInfo> sceneInfos; Containers::Array<SceneInfo> sceneInfos;
Containers::Array<UnsignedInt> materialReferenceCount{importer->materialCount()}; Containers::Array<UnsignedInt> materialReferenceCount;
Containers::Array<UnsignedInt> lightReferenceCount{importer->lightCount()}; Containers::Array<UnsignedInt> lightReferenceCount;
Containers::Array<UnsignedInt> meshReferenceCount{importer->meshCount()}; Containers::Array<UnsignedInt> meshReferenceCount;
Containers::Array<UnsignedInt> skinReferenceCount{importer->skin3DCount()}; Containers::Array<UnsignedInt> skinReferenceCount;
if(args.isSet("info") || args.isSet("info-scenes") || args.isSet("info-materials") || args.isSet("info-lights") || args.isSet("info-meshes") || args.isSet("info-skins")) for(UnsignedInt i = 0; i != importer->sceneCount(); ++i) { if(args.isSet("info") || args.isSet("info-scenes")) {
Containers::Optional<Trade::SceneData> scene = importer->scene(i); materialReferenceCount = Containers::Array<UnsignedInt>{importer->materialCount()};
if(!scene) continue; lightReferenceCount = Containers::Array<UnsignedInt>{importer->lightCount()};
meshReferenceCount = Containers::Array<UnsignedInt>{importer->meshCount()};
SceneInfo info{}; skinReferenceCount = Containers::Array<UnsignedInt>{importer->skin3DCount()};
info.scene = i;
info.mappingType = scene->mappingType(); for(UnsignedInt i = 0; i != importer->sceneCount(); ++i) {
info.mappingBound = scene->mappingBound(); Containers::Optional<Trade::SceneData> scene = importer->scene(i);
info.dataSize = scene->data().size(); if(!scene) continue;
info.name = importer->sceneName(i);
for(UnsignedInt j = 0; j != scene->fieldCount(); ++j) { SceneInfo info{};
const Trade::SceneField name = scene->fieldName(j); info.scene = i;
info.mappingType = scene->mappingType();
if(name == Trade::SceneField::Mesh) for(const Containers::Pair<UnsignedInt, Containers::Pair<UnsignedInt, Int>>& meshMaterial: scene->meshesMaterialsAsArray()) { info.mappingBound = scene->mappingBound();
if(meshMaterial.first() < meshReferenceCount.size()) info.dataSize = scene->data().size();
++meshReferenceCount[meshMaterial.first()]; info.name = importer->sceneName(i);
if(UnsignedInt(meshMaterial.second().second()) < materialReferenceCount.size()) for(UnsignedInt j = 0; j != scene->fieldCount(); ++j) {
++materialReferenceCount[meshMaterial.second().second()]; const Trade::SceneField name = scene->fieldName(j);
}
if(name == Trade::SceneField::Mesh) for(const Containers::Pair<UnsignedInt, Containers::Pair<UnsignedInt, Int>>& meshMaterial: scene->meshesMaterialsAsArray()) {
if(meshMaterial.first() < meshReferenceCount.size())
++meshReferenceCount[meshMaterial.first()];
if(UnsignedInt(meshMaterial.second().second()) < materialReferenceCount.size())
++materialReferenceCount[meshMaterial.second().second()];
}
if(name == Trade::SceneField::Skin) for(const Containers::Pair<UnsignedInt, UnsignedInt> skin: scene->skinsAsArray()) { if(name == Trade::SceneField::Skin) for(const Containers::Pair<UnsignedInt, UnsignedInt> skin: scene->skinsAsArray()) {
if(skin.second() < skinReferenceCount.size()) if(skin.second() < skinReferenceCount.size())
++skinReferenceCount[skin.second()]; ++skinReferenceCount[skin.second()];
/** @todo 2D/3D distinction */ /** @todo 2D/3D distinction */
} }
if(name == Trade::SceneField::Light) for(const Containers::Pair<UnsignedInt, UnsignedInt>& light: scene->lightsAsArray()) { if(name == Trade::SceneField::Light) for(const Containers::Pair<UnsignedInt, UnsignedInt>& light: scene->lightsAsArray()) {
if(light.second() < lightReferenceCount.size()) if(light.second() < lightReferenceCount.size())
++lightReferenceCount[light.second()]; ++lightReferenceCount[light.second()];
} }
arrayAppend(info.fields, InPlaceInit, arrayAppend(info.fields, InPlaceInit,
name, name,
Trade::isSceneFieldCustom(name) ? Trade::isSceneFieldCustom(name) ?
importer->sceneFieldName(name) : "", importer->sceneFieldName(name) : "",
scene->fieldFlags(j), scene->fieldFlags(j),
scene->fieldType(j), scene->fieldType(j),
scene->fieldArraySize(j), scene->fieldArraySize(j),
scene->fieldSize(j)); scene->fieldSize(j));
} }
/* Add it to the array only if scene info was requested. We're
going through this loop also if just light / material / mesh /
skin info is requested, to gather reference count */
if(args.isSet("info") || args.isSet("info-scenes"))
arrayAppend(sceneInfos, std::move(info)); arrayAppend(sceneInfos, std::move(info));
}
} }
/* Animation properties */ /* Animation properties */
@ -489,7 +502,6 @@ used.)")
SkinInfo info{}; SkinInfo info{};
info.skin = i; info.skin = i;
info.name = importer->skin3DName(i); info.name = importer->skin3DName(i);
info.references = skinReferenceCount[i];
info.data = *std::move(skin); info.data = *std::move(skin);
arrayAppend(skinInfos, std::move(info)); arrayAppend(skinInfos, std::move(info));
@ -510,48 +522,52 @@ used.)")
LightInfo info{}; LightInfo info{};
info.light = i; info.light = i;
info.name = importer->lightName(i); info.name = importer->lightName(i);
info.references = lightReferenceCount[i];
info.data = *std::move(light); info.data = *std::move(light);
arrayAppend(lightInfos, std::move(info)); arrayAppend(lightInfos, std::move(info));
} }
/* Material properties */ /* Material properties, together with how much is each texture shared
(which gets used only if both --info-materials and --info-textures
is passed). */
Containers::Array<MaterialInfo> materialInfos; Containers::Array<MaterialInfo> materialInfos;
Containers::Array<UnsignedInt> textureReferenceCount{importer->textureCount()}; Containers::Array<UnsignedInt> textureReferenceCount;
if(args.isSet("info") || args.isSet("info-materials")) for(UnsignedInt i = 0; i != importer->materialCount(); ++i) { if(args.isSet("info") || args.isSet("info-materials")) {
Containers::Optional<Trade::MaterialData> material; textureReferenceCount = Containers::Array<UnsignedInt>{importer->textureCount()};
{
Duration d{importTime};
if(!(material = importer->material(i))) {
error = true;
continue;
}
}
/* Calculate texture reference count for all properties that for(UnsignedInt i = 0; i != importer->materialCount(); ++i) {
look like a texture */ Containers::Optional<Trade::MaterialData> material;
for(UnsignedInt j = 0; j != material->layerCount(); ++j) { {
for(UnsignedInt k = 0; k != material->attributeCount(j); ++k) { Duration d{importTime};
if(material->attributeType(j, k) != Trade::MaterialAttributeType::UnsignedInt || !Utility::String::endsWith(material->attributeName(j, k), "Texture")) if(!(material = importer->material(i))) {
error = true;
continue; continue;
}
}
const UnsignedInt texture = material->attribute<UnsignedInt>(j, k); /* Calculate texture reference count for all properties that
/** @todo once StridedBitArrayView2D exists, fix this to look like a texture */
count each material only once by having one bit for for(UnsignedInt j = 0; j != material->layerCount(); ++j) {
every material and texture */ for(UnsignedInt k = 0; k != material->attributeCount(j); ++k) {
if(texture < textureReferenceCount.size()) if(material->attributeType(j, k) != Trade::MaterialAttributeType::UnsignedInt || !Utility::String::endsWith(material->attributeName(j, k), "Texture"))
++textureReferenceCount[texture]; continue;
const UnsignedInt texture = material->attribute<UnsignedInt>(j, k);
/** @todo once StridedBitArrayView2D exists, fix this
to count each material only once by having one bit
for every material and texture */
if(texture < textureReferenceCount.size())
++textureReferenceCount[texture];
}
} }
}
MaterialInfo info{}; MaterialInfo info{};
info.material = i; info.material = i;
info.name = importer->materialName(i); info.name = importer->materialName(i);
info.references = materialReferenceCount[i]; info.data = *std::move(material);
info.data = *std::move(material);
arrayAppend(materialInfos, std::move(info)); arrayAppend(materialInfos, std::move(info));
}
} }
/* Mesh properties */ /* Mesh properties */
@ -575,7 +591,6 @@ used.)")
info.vertexDataSize = mesh->vertexData().size(); info.vertexDataSize = mesh->vertexData().size();
if(!j) { if(!j) {
info.name = importer->meshName(i); info.name = importer->meshName(i);
info.references = meshReferenceCount[i];
} }
if(mesh->isIndexed()) { if(mesh->isIndexed()) {
info.indexCount = mesh->indexCount(); info.indexCount = mesh->indexCount();
@ -653,7 +668,6 @@ used.)")
TextureInfo info{}; TextureInfo info{};
info.texture = i; info.texture = i;
info.name = importer->textureName(i); info.name = importer->textureName(i);
info.references = textureReferenceCount[i];
info.data = *std::move(texture); info.data = *std::move(texture);
arrayAppend(textureInfos, std::move(info)); arrayAppend(textureInfos, std::move(info));
@ -720,10 +734,12 @@ used.)")
for(const SkinInfo& info: skinInfos) { for(const SkinInfo& info: skinInfos) {
Debug d; Debug d;
d << "Skin" << info.skin; d << "Skin" << info.skin;
/* Print reference count only if there actually is a scene,
otherwise this information is useless */ /* Print reference count only if there actually are scenes and they
if(importer->objectCount()) were parsed, otherwise this information is useless */
d << Utility::formatString("(referenced by {} objects)", info.references); if(skinReferenceCount)
d << Utility::formatString("(referenced by {} objects)", skinReferenceCount[info.skin]);
d << Debug::nospace << ":"; d << Debug::nospace << ":";
if(!info.name.empty()) d << info.name; if(!info.name.empty()) d << info.name;
@ -733,10 +749,12 @@ used.)")
for(const LightInfo& info: lightInfos) { for(const LightInfo& info: lightInfos) {
Debug d; Debug d;
d << "Light" << info.light; d << "Light" << info.light;
/* Print reference count only if there actually is a scene,
otherwise this information is useless */ /* Print reference count only if there actually are scenes and they
if(importer->objectCount()) were parsed, otherwise this information is useless */
d << Utility::formatString("(referenced by {} objects)", info.references); if(lightReferenceCount)
d << Utility::formatString("(referenced by {} objects)", lightReferenceCount[info.light]);
d << Debug::nospace << ":"; d << Debug::nospace << ":";
if(!info.name.empty()) d << info.name; if(!info.name.empty()) d << info.name;
@ -752,10 +770,12 @@ used.)")
for(const MaterialInfo& info: materialInfos) { for(const MaterialInfo& info: materialInfos) {
Debug d; Debug d;
d << "Material" << info.material; d << "Material" << info.material;
/* Print reference count only if there actually is a scene,
otherwise this information is useless */ /* Print reference count only if there actually are scenes and they
if(importer->objectCount()) were parsed, otherwise this information is useless */
d << Utility::formatString("(referenced by {} objects)", info.references); if(materialReferenceCount)
d << Utility::formatString("(referenced by {} objects)", materialReferenceCount[info.material]);
d << Debug::nospace << ":"; d << Debug::nospace << ":";
if(!info.name.empty()) d << info.name; if(!info.name.empty()) d << info.name;
@ -835,10 +855,12 @@ used.)")
Debug d; Debug d;
if(info.level == 0) { if(info.level == 0) {
d << "Mesh" << info.mesh; d << "Mesh" << info.mesh;
/* Print reference count only if there actually is a scene,
otherwise this information is useless */ /* Print reference count only if there actually are scenes and
if(importer->objectCount()) they were parsed, otherwise this information is useless */
d << Utility::formatString("(referenced by {} objects)", info.references); if(meshReferenceCount)
d << Utility::formatString("(referenced by {} objects)", meshReferenceCount[info.mesh]);
d << Debug::nospace << ":"; d << Debug::nospace << ":";
if(!info.name.empty()) d << info.name; if(!info.name.empty()) d << info.name;
d << Debug::newline; d << Debug::newline;
@ -875,10 +897,12 @@ used.)")
for(const TextureInfo& info: textureInfos) { for(const TextureInfo& info: textureInfos) {
Debug d; Debug d;
d << "Texture" << info.texture; d << "Texture" << info.texture;
/* Print reference count only if there actually are some
materials, otherwise this information is useless */ /* Print reference count only if there actually are materials and
if(importer->materialCount()) they were parsed, otherwise this information is useless */
d << Utility::formatString("(referenced by {} material attributes)", info.references); if(textureReferenceCount)
d << Utility::formatString("(referenced by {} material attributes)", textureReferenceCount[info.texture]);
d << Debug::nospace << ":"; d << Debug::nospace << ":";
if(!info.name.empty()) d << info.name; if(!info.name.empty()) d << info.name;
d << Debug::newline; d << Debug::newline;

Loading…
Cancel
Save