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
- `--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
accept a comma-separated list of key/value pairs to set in the importer /
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.
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
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
@ -330,28 +344,24 @@ used.)")
struct SkinInfo {
UnsignedInt skin;
UnsignedInt references;
Trade::SkinData3D data{{}, {}};
std::string name;
};
struct LightInfo {
UnsignedInt light;
UnsignedInt references;
Trade::LightData data{{}, {}, {}};
std::string name;
};
struct MaterialInfo {
UnsignedInt material;
UnsignedInt references;
Trade::MaterialData data{{}, {}};
std::string name;
};
struct TextureInfo {
UnsignedInt texture;
UnsignedInt references;
Trade::TextureData data{{}, {}, {}, {}, {}, {}};
std::string name;
};
@ -367,7 +377,6 @@ used.)")
struct MeshInfo {
UnsignedInt mesh, level;
UnsignedInt references;
MeshPrimitive primitive;
UnsignedInt indexCount, vertexCount;
MeshIndexType indexType;
@ -398,59 +407,63 @@ used.)")
/* Parse everything first to avoid errors interleaved with output */
/* Scene properties, together with counting how much is each mesh /
light / material / skin shared. Texture reference count is
calculated when parsing materials. */
light / material / skin shared (which gets used only if both
--info-scenes and --info-{lights,materials,skins} is passed).
Texture reference count is calculated when parsing materials. */
Containers::Array<SceneInfo> sceneInfos;
Containers::Array<UnsignedInt> materialReferenceCount{importer->materialCount()};
Containers::Array<UnsignedInt> lightReferenceCount{importer->lightCount()};
Containers::Array<UnsignedInt> meshReferenceCount{importer->meshCount()};
Containers::Array<UnsignedInt> skinReferenceCount{importer->skin3DCount()};
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) {
Containers::Optional<Trade::SceneData> scene = importer->scene(i);
if(!scene) continue;
SceneInfo info{};
info.scene = i;
info.mappingType = scene->mappingType();
info.mappingBound = scene->mappingBound();
info.dataSize = scene->data().size();
info.name = importer->sceneName(i);
for(UnsignedInt j = 0; j != scene->fieldCount(); ++j) {
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()];
}
Containers::Array<UnsignedInt> materialReferenceCount;
Containers::Array<UnsignedInt> lightReferenceCount;
Containers::Array<UnsignedInt> meshReferenceCount;
Containers::Array<UnsignedInt> skinReferenceCount;
if(args.isSet("info") || args.isSet("info-scenes")) {
materialReferenceCount = Containers::Array<UnsignedInt>{importer->materialCount()};
lightReferenceCount = Containers::Array<UnsignedInt>{importer->lightCount()};
meshReferenceCount = Containers::Array<UnsignedInt>{importer->meshCount()};
skinReferenceCount = Containers::Array<UnsignedInt>{importer->skin3DCount()};
for(UnsignedInt i = 0; i != importer->sceneCount(); ++i) {
Containers::Optional<Trade::SceneData> scene = importer->scene(i);
if(!scene) continue;
SceneInfo info{};
info.scene = i;
info.mappingType = scene->mappingType();
info.mappingBound = scene->mappingBound();
info.dataSize = scene->data().size();
info.name = importer->sceneName(i);
for(UnsignedInt j = 0; j != scene->fieldCount(); ++j) {
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(skin.second() < skinReferenceCount.size())
++skinReferenceCount[skin.second()];
/** @todo 2D/3D distinction */
}
if(name == Trade::SceneField::Skin) for(const Containers::Pair<UnsignedInt, UnsignedInt> skin: scene->skinsAsArray()) {
if(skin.second() < skinReferenceCount.size())
++skinReferenceCount[skin.second()];
/** @todo 2D/3D distinction */
}
if(name == Trade::SceneField::Light) for(const Containers::Pair<UnsignedInt, UnsignedInt>& light: scene->lightsAsArray()) {
if(light.second() < lightReferenceCount.size())
++lightReferenceCount[light.second()];
}
if(name == Trade::SceneField::Light) for(const Containers::Pair<UnsignedInt, UnsignedInt>& light: scene->lightsAsArray()) {
if(light.second() < lightReferenceCount.size())
++lightReferenceCount[light.second()];
}
arrayAppend(info.fields, InPlaceInit,
name,
Trade::isSceneFieldCustom(name) ?
importer->sceneFieldName(name) : "",
scene->fieldFlags(j),
scene->fieldType(j),
scene->fieldArraySize(j),
scene->fieldSize(j));
}
arrayAppend(info.fields, InPlaceInit,
name,
Trade::isSceneFieldCustom(name) ?
importer->sceneFieldName(name) : "",
scene->fieldFlags(j),
scene->fieldType(j),
scene->fieldArraySize(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));
}
}
/* Animation properties */
@ -489,7 +502,6 @@ used.)")
SkinInfo info{};
info.skin = i;
info.name = importer->skin3DName(i);
info.references = skinReferenceCount[i];
info.data = *std::move(skin);
arrayAppend(skinInfos, std::move(info));
@ -510,48 +522,52 @@ used.)")
LightInfo info{};
info.light = i;
info.name = importer->lightName(i);
info.references = lightReferenceCount[i];
info.data = *std::move(light);
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<UnsignedInt> textureReferenceCount{importer->textureCount()};
if(args.isSet("info") || args.isSet("info-materials")) for(UnsignedInt i = 0; i != importer->materialCount(); ++i) {
Containers::Optional<Trade::MaterialData> material;
{
Duration d{importTime};
if(!(material = importer->material(i))) {
error = true;
continue;
}
}
Containers::Array<UnsignedInt> textureReferenceCount;
if(args.isSet("info") || args.isSet("info-materials")) {
textureReferenceCount = Containers::Array<UnsignedInt>{importer->textureCount()};
/* Calculate texture reference count for all properties that
look like a texture */
for(UnsignedInt j = 0; j != material->layerCount(); ++j) {
for(UnsignedInt k = 0; k != material->attributeCount(j); ++k) {
if(material->attributeType(j, k) != Trade::MaterialAttributeType::UnsignedInt || !Utility::String::endsWith(material->attributeName(j, k), "Texture"))
for(UnsignedInt i = 0; i != importer->materialCount(); ++i) {
Containers::Optional<Trade::MaterialData> material;
{
Duration d{importTime};
if(!(material = importer->material(i))) {
error = true;
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];
/* Calculate texture reference count for all properties that
look like a texture */
for(UnsignedInt j = 0; j != material->layerCount(); ++j) {
for(UnsignedInt k = 0; k != material->attributeCount(j); ++k) {
if(material->attributeType(j, k) != Trade::MaterialAttributeType::UnsignedInt || !Utility::String::endsWith(material->attributeName(j, k), "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{};
info.material = i;
info.name = importer->materialName(i);
info.references = materialReferenceCount[i];
info.data = *std::move(material);
MaterialInfo info{};
info.material = i;
info.name = importer->materialName(i);
info.data = *std::move(material);
arrayAppend(materialInfos, std::move(info));
arrayAppend(materialInfos, std::move(info));
}
}
/* Mesh properties */
@ -575,7 +591,6 @@ used.)")
info.vertexDataSize = mesh->vertexData().size();
if(!j) {
info.name = importer->meshName(i);
info.references = meshReferenceCount[i];
}
if(mesh->isIndexed()) {
info.indexCount = mesh->indexCount();
@ -653,7 +668,6 @@ used.)")
TextureInfo info{};
info.texture = i;
info.name = importer->textureName(i);
info.references = textureReferenceCount[i];
info.data = *std::move(texture);
arrayAppend(textureInfos, std::move(info));
@ -720,10 +734,12 @@ used.)")
for(const SkinInfo& info: skinInfos) {
Debug d;
d << "Skin" << info.skin;
/* Print reference count only if there actually is a scene,
otherwise this information is useless */
if(importer->objectCount())
d << Utility::formatString("(referenced by {} objects)", info.references);
/* Print reference count only if there actually are scenes and they
were parsed, otherwise this information is useless */
if(skinReferenceCount)
d << Utility::formatString("(referenced by {} objects)", skinReferenceCount[info.skin]);
d << Debug::nospace << ":";
if(!info.name.empty()) d << info.name;
@ -733,10 +749,12 @@ used.)")
for(const LightInfo& info: lightInfos) {
Debug d;
d << "Light" << info.light;
/* Print reference count only if there actually is a scene,
otherwise this information is useless */
if(importer->objectCount())
d << Utility::formatString("(referenced by {} objects)", info.references);
/* Print reference count only if there actually are scenes and they
were parsed, otherwise this information is useless */
if(lightReferenceCount)
d << Utility::formatString("(referenced by {} objects)", lightReferenceCount[info.light]);
d << Debug::nospace << ":";
if(!info.name.empty()) d << info.name;
@ -752,10 +770,12 @@ used.)")
for(const MaterialInfo& info: materialInfos) {
Debug d;
d << "Material" << info.material;
/* Print reference count only if there actually is a scene,
otherwise this information is useless */
if(importer->objectCount())
d << Utility::formatString("(referenced by {} objects)", info.references);
/* Print reference count only if there actually are scenes and they
were parsed, otherwise this information is useless */
if(materialReferenceCount)
d << Utility::formatString("(referenced by {} objects)", materialReferenceCount[info.material]);
d << Debug::nospace << ":";
if(!info.name.empty()) d << info.name;
@ -835,10 +855,12 @@ used.)")
Debug d;
if(info.level == 0) {
d << "Mesh" << info.mesh;
/* Print reference count only if there actually is a scene,
otherwise this information is useless */
if(importer->objectCount())
d << Utility::formatString("(referenced by {} objects)", info.references);
/* Print reference count only if there actually are scenes and
they were parsed, otherwise this information is useless */
if(meshReferenceCount)
d << Utility::formatString("(referenced by {} objects)", meshReferenceCount[info.mesh]);
d << Debug::nospace << ":";
if(!info.name.empty()) d << info.name;
d << Debug::newline;
@ -875,10 +897,12 @@ used.)")
for(const TextureInfo& info: textureInfos) {
Debug d;
d << "Texture" << info.texture;
/* Print reference count only if there actually are some
materials, otherwise this information is useless */
if(importer->materialCount())
d << Utility::formatString("(referenced by {} material attributes)", info.references);
/* Print reference count only if there actually are materials and
they were parsed, otherwise this information is useless */
if(textureReferenceCount)
d << Utility::formatString("(referenced by {} material attributes)", textureReferenceCount[info.texture]);
d << Debug::nospace << ":";
if(!info.name.empty()) d << info.name;
d << Debug::newline;

Loading…
Cancel
Save