From ee9e2e96be3ba756766ab857a44fccc5316c1d92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 18 Jan 2023 21:08:41 +0100 Subject: [PATCH] sceneconverter: implement a --phong-to-pbr option. Exposes MaterialTools::phongToPbrMetallicRoughness() that got added some time ago. Most of the code and tests is scaffolding needed for direct material import and processing outside of the addImporterContents() automagic, similar to what was already done for meshes and images. In particular, adding more material conversion options such as canonicalization or deduplication will be significantly easier now that the basics are done and tested. --- CMakeLists.txt | 2 +- doc/changelog.dox | 3 + src/Magnum/SceneTools/CMakeLists.txt | 1 + src/Magnum/SceneTools/Test/CMakeLists.txt | 6 + .../SceneTools/Test/SceneConverterTest.cpp | 103 ++++++++++++++- .../broken-material.gltf | 12 ++ .../ignoring-unsupported.gltf | 9 ++ .../SceneConverterTestFiles/materials-3d.gltf | 35 +++++ .../materials-pbr.gltf | 122 ++++++++++++++++++ .../materials-phong.mtl | 7 + .../materials-phong.obj | 15 +++ ...materials-separate-metalness-roughness.mtl | 9 ++ src/Magnum/SceneTools/sceneconverter.cpp | 100 +++++++++++++- 13 files changed, 416 insertions(+), 8 deletions(-) create mode 100644 src/Magnum/SceneTools/Test/SceneConverterTestFiles/broken-material.gltf create mode 100644 src/Magnum/SceneTools/Test/SceneConverterTestFiles/materials-3d.gltf create mode 100644 src/Magnum/SceneTools/Test/SceneConverterTestFiles/materials-pbr.gltf create mode 100644 src/Magnum/SceneTools/Test/SceneConverterTestFiles/materials-phong.mtl create mode 100644 src/Magnum/SceneTools/Test/SceneConverterTestFiles/materials-phong.obj create mode 100644 src/Magnum/SceneTools/Test/SceneConverterTestFiles/materials-separate-metalness-roughness.mtl diff --git a/CMakeLists.txt b/CMakeLists.txt index d94148287..75d5d4431 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -205,7 +205,7 @@ cmake_dependent_option(MAGNUM_WITH_TGAIMPORTER "Build TgaImporter plugin" OFF "N # Parts of the library cmake_dependent_option(MAGNUM_WITH_AUDIO "Build Audio library" OFF "NOT MAGNUM_WITH_AL_INFO;NOT MAGNUM_WITH_ANYAUDIOIMPORTER;NOT MAGNUM_WITH_WAVAUDIOIMPORTER" ON) option(MAGNUM_WITH_DEBUGTOOLS "Build DebugTools library" ON) -option(MAGNUM_WITH_MATERIALTOOLS "Build MaterialTools library" ON) +cmake_dependent_option(MAGNUM_WITH_MATERIALTOOLS "Build MaterialTools library" ON "NOT MAGNUM_WITH_SCENECONVERTER" ON) cmake_dependent_option(MAGNUM_WITH_MESHTOOLS "Build MeshTools library" ON "NOT MAGNUM_WITH_OBJIMPORTER;NOT MAGNUM_WITH_SCENECONVERTER" ON) option(MAGNUM_WITH_SCENEGRAPH "Build SceneGraph library" ON) cmake_dependent_option(MAGNUM_WITH_SCENETOOLS "Build SceneTools library" ON "NOT MAGNUM_WITH_SCENECONVERTER" ON) diff --git a/doc/changelog.dox b/doc/changelog.dox index e972472d2..58fe63dc9 100644 --- a/doc/changelog.dox +++ b/doc/changelog.dox @@ -235,6 +235,9 @@ See also: experimental `--concatenate-meshes` option, which will flatten the mesh hierarchy and concatenate all meshes together. Note that it doesn't correctly handle all corner cases yet and may assert on certain inputs. +- Added a `--phong-to-pbr` option to the @ref magnum-sceneconverter "magnum-sceneconverter" + utility to perform conversion of Phong materials to PBR, useful for example + when converting old OBJ and COLLADA files to glTF - The @ref magnum-sceneconverter "magnum-sceneconverter" `--info` output is now more compact and colored for better readability - Added `--info-importer`, `--info-converter` and `--info-image-converter` diff --git a/src/Magnum/SceneTools/CMakeLists.txt b/src/Magnum/SceneTools/CMakeLists.txt index db90132ef..6ffad5730 100644 --- a/src/Magnum/SceneTools/CMakeLists.txt +++ b/src/Magnum/SceneTools/CMakeLists.txt @@ -99,6 +99,7 @@ if(MAGNUM_WITH_SCENECONVERTER) target_link_libraries(magnum-sceneconverter PRIVATE Corrade::Main Magnum + MagnumMaterialTools MagnumMeshTools MagnumSceneTools MagnumTrade diff --git a/src/Magnum/SceneTools/Test/CMakeLists.txt b/src/Magnum/SceneTools/Test/CMakeLists.txt index 4239f0fed..eeaab8930 100644 --- a/src/Magnum/SceneTools/Test/CMakeLists.txt +++ b/src/Magnum/SceneTools/Test/CMakeLists.txt @@ -97,6 +97,7 @@ if(CORRADE_TARGET_UNIX AND NOT CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT) SceneConverterTestFiles/blue4x4x1.ktx2 SceneConverterTestFiles/broken-image-2d.gltf SceneConverterTestFiles/broken-image-3d.gltf + SceneConverterTestFiles/broken-material.gltf SceneConverterTestFiles/broken-mesh.obj SceneConverterTestFiles/broken-scene.gltf SceneConverterTestFiles/dxt1.dds @@ -115,6 +116,11 @@ if(CORRADE_TARGET_UNIX AND NOT CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT) SceneConverterTestFiles/info-image-converter.txt SceneConverterTestFiles/info-importer.txt SceneConverterTestFiles/info-importer-ignored-input-output.txt + SceneConverterTestFiles/materials-3d.gltf + SceneConverterTestFiles/materials-pbr.gltf + SceneConverterTestFiles/materials-phong.mtl + SceneConverterTestFiles/materials-phong.obj + SceneConverterTestFiles/materials-separate-metalness-roughness.mtl SceneConverterTestFiles/point.obj SceneConverterTestFiles/quad-duplicates-fuzzy.obj SceneConverterTestFiles/quad-duplicates.obj diff --git a/src/Magnum/SceneTools/Test/SceneConverterTest.cpp b/src/Magnum/SceneTools/Test/SceneConverterTest.cpp index 0c06d686f..85e7b7ed9 100644 --- a/src/Magnum/SceneTools/Test/SceneConverterTest.cpp +++ b/src/Magnum/SceneTools/Test/SceneConverterTest.cpp @@ -664,6 +664,48 @@ const struct { "Processing 3D image 1 with StbResizeImageConverter...\n" "Trade::AbstractSceneConverter::addImporterContents(): adding texture 0 out of 2\n" "Trade::AbstractSceneConverter::addImporterContents(): adding texture 1 out of 2\n"}, + {"Phong to PBR", {InPlaceInit, { + "-I", "UfbxImporter", "-C", "GltfSceneConverter", "--phong-to-pbr", + /* We need the file as minimal as possible, so no index buffer. + OTOH, dropping meshes altogether would also lose + node/mesh/material assignment, which is important */ + "-i", "generateIndices=false", + /* Removing the generator identifier for a smaller file */ + "-c", "generator=", + Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/materials-phong.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/materials-pbr.gltf") + }}, + "UfbxImporter", "PngImporter", "GltfSceneConverter", {"PngImageConverter", nullptr}, nullptr, + /* The file should contain also material names and everything */ + "materials-pbr.gltf", nullptr, + "MaterialTools::phongToPbrMetallicRoughness(): unconvertable Trade::MaterialAttribute::AmbientColor attribute, skipping\n" + /** @todo remove this once StridedBitArray exists and SceneData + + UfbxImporter uses it for bit values */ + "Trade::GltfSceneConverter::add(): custom scene field Visibility has unsupported type Trade::SceneFieldType::UnsignedByte, skipping\n" + "Trade::GltfSceneConverter::add(): custom scene field GeometryTransformHelper has unsupported type Trade::SceneFieldType::UnsignedByte, skipping\n"}, + {"Phong to PBR, verbose", {InPlaceInit, { + /* Same as above, just with -v added */ + "-I", "UfbxImporter", "-C", "GltfSceneConverter", "--phong-to-pbr", + "-i", "generateIndices=false", "-c", "generator=", "-v", + Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/materials-phong.obj"), Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/materials-pbr.gltf") + }}, + "UfbxImporter", "PngImporter", "GltfSceneConverter", {"PngImageConverter", nullptr}, nullptr, + "materials-pbr.gltf", nullptr, + "Converting material 0 to PBR\n" + "MaterialTools::phongToPbrMetallicRoughness(): unconvertable Trade::MaterialAttribute::AmbientColor attribute, skipping\n" + "Converting material 1 to PBR\n" + "Trade::AbstractSceneConverter::addImporterContents(): adding 2D image 0 out of 2\n" + "Trade::AnyImageImporter::openFile(): using PngImporter\n" + "Trade::AbstractSceneConverter::addImporterContents(): adding 2D image 1 out of 2\n" + "Trade::AnyImageImporter::openFile(): using PngImporter\n" + "Trade::AbstractSceneConverter::addImporterContents(): adding texture 0 out of 2\n" + "Trade::AbstractSceneConverter::addImporterContents(): adding texture 1 out of 2\n" + "Trade::AbstractSceneConverter::addImporterContents(): adding mesh 0 out of 2\n" + "Trade::AbstractSceneConverter::addImporterContents(): adding mesh 1 out of 2\n" + "Trade::AbstractSceneConverter::addImporterContents(): adding scene 0 out of 1\n" + /** @todo remove this once StridedBitArray exists and SceneData + + UfbxImporter uses it for bit values */ + "Trade::GltfSceneConverter::add(): custom scene field Visibility has unsupported type Trade::SceneFieldType::UnsignedByte, skipping\n" + "Trade::GltfSceneConverter::add(): custom scene field GeometryTransformHelper has unsupported type Trade::SceneFieldType::UnsignedByte, skipping\n"}, {"data unsupported by the converter", {InPlaceInit, { "-I", "GltfImporter", "-i", "experimentalKhrTextureKtx", "-C", "StanfordSceneConverter", @@ -675,7 +717,8 @@ const struct { "quad.ply", nullptr, "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring 1 2D images not supported by the converter\n" "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring 1 3D images not supported by the converter\n" - "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring 2 textures not supported by the converter\n"}, + "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring 2 textures not supported by the converter\n" + "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring 1 materials not supported by the converter\n"}, {"per-image processed images unsupported by the converter", {InPlaceInit, { "-I", "GltfImporter", "-i", "experimentalKhrTextureKtx", "-P", "StbResizeImageConverter", "-p", "size=\"1 1\"", @@ -690,7 +733,23 @@ const struct { printed by sceneconverter itself, not the converter interface */ "Ignoring 1 2D images not supported by the converter\n" "Ignoring 1 3D images not supported by the converter\n" - "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring 2 textures not supported by the converter\n"}, + "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring 2 textures not supported by the converter\n" + "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring 1 materials not supported by the converter\n"}, + {"per-material processed materials unsupported by the converter", {InPlaceInit, { + "-I", "GltfImporter", "-i", "experimentalKhrTextureKtx", + "--phong-to-pbr", "-C", "StanfordSceneConverter", + Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/ignoring-unsupported.gltf"), + Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/quad.ply") + }}, + "GltfImporter", "KtxImporter", "StanfordSceneConverter", + {"StbResizeImageConverter", nullptr}, nullptr, + "quad.ply", nullptr, + "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring 1 2D images not supported by the converter\n" + "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring 1 3D images not supported by the converter\n" + "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring 2 textures not supported by the converter\n" + /* Compared to "data unsupported by the converter" this message is + printed by sceneconverter itself, not the converter interface */ + "Ignoring 1 materials not supported by the converter\n"}, }; const struct { @@ -978,6 +1037,46 @@ const struct { nullptr, "KtxImporter", nullptr, nullptr, "Sorry, 1D image conversion is not implemented yet\n"}, + {"can't load a material for Phong to PBR conversion", {InPlaceInit, { + "-I", "GltfImporter", "--phong-to-pbr", + Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/broken-material.gltf"), + Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.gltf") + }}, + "GltfImporter", nullptr, nullptr, + nullptr, + "Trade::GltfImporter::material(): unrecognized alphaMode TOUGH\n" + "Cannot import material 2\n"}, + {"can't add material dependencies", {InPlaceInit, { + /* --phong-to-pbr is a no-op because the input is PBR already, we + just need something that causes the materials to be added + directly */ + "-I", "GltfImporter", "--phong-to-pbr", + /* Not enabling experimentalKhrTextureKtx for the converter in + order to trigger this error */ + "-i", "experimentalKhrTextureKtx", "-C", "GltfSceneConverter", + Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/materials-3d.gltf"), + Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.gltf") + }}, + "GltfImporter", "KtxImporter", "GltfSceneConverter", + nullptr, + "Trade::AbstractSceneConverter::addSupportedImporterContents(): ignoring 1 3D images not supported by the converter\n" + /* Another way this could fail is that the texture is now referencing a + 3D image out of bounds (because adding it failed above) */ + "Trade::GltfSceneConverter::add(): 2D array textures require experimentalKhrTextureKtx to be enabled\n" + "Cannot add material dependencies\n"}, + {"can't add processed material", {InPlaceInit, { + /* --phong-to-pbr is a no-op because the input is PBR already, we + just need something that causes the materials to be added + directly */ + "-I", "UfbxImporter", "--phong-to-pbr", + "-C", "GltfSceneConverter", + Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/materials-separate-metalness-roughness.mtl"), + Utility::Path::join(SCENETOOLS_TEST_OUTPUT_DIR, "SceneConverterTestFiles/whatever.gltf") + }}, + "UfbxImporter", "PngImporter", "GltfSceneConverter", + nullptr, + "Trade::GltfSceneConverter::add(): unsupported R/R packing of a metallic/roughness texture\n" + "Cannot add material 1\n"} }; SceneConverterTest::SceneConverterTest() { diff --git a/src/Magnum/SceneTools/Test/SceneConverterTestFiles/broken-material.gltf b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/broken-material.gltf new file mode 100644 index 000000000..3731ae77d --- /dev/null +++ b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/broken-material.gltf @@ -0,0 +1,12 @@ +{ + "asset": { + "version": "2.0" + }, + "materials": [ + {}, + {}, + { + "alphaMode": "TOUGH" + } + ] +} diff --git a/src/Magnum/SceneTools/Test/SceneConverterTestFiles/ignoring-unsupported.gltf b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/ignoring-unsupported.gltf index 1cb4f720c..a97dd2d1a 100644 --- a/src/Magnum/SceneTools/Test/SceneConverterTestFiles/ignoring-unsupported.gltf +++ b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/ignoring-unsupported.gltf @@ -73,5 +73,14 @@ } } } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 0 + } + } + } ] } diff --git a/src/Magnum/SceneTools/Test/SceneConverterTestFiles/materials-3d.gltf b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/materials-3d.gltf new file mode 100644 index 000000000..54a44dc08 --- /dev/null +++ b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/materials-3d.gltf @@ -0,0 +1,35 @@ +{ + "asset": { + "version": "2.0" + }, + "extensionsUsed": [ + "KHR_texture_ktx" + ], + "extensionsRequired": [ + "KHR_texture_ktx" + ], + "textures": [ + { + "extensions": { + "KHR_texture_ktx": { + "source": 0, + "layer": 0 + } + } + } + ], + "images": [ + { + "uri": "red2x2x1.ktx2" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 0 + } + } + } + ] +} diff --git a/src/Magnum/SceneTools/Test/SceneConverterTestFiles/materials-pbr.gltf b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/materials-pbr.gltf new file mode 100644 index 000000000..1bfc99080 --- /dev/null +++ b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/materials-pbr.gltf @@ -0,0 +1,122 @@ +{ + "asset": { + "version": "2.0" + }, + "buffers": [ + { + "uri": "materials-pbr.bin", + "byteLength": 72 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 36 + }, + { + "buffer": 0, + "byteOffset": 36, + "byteLength": 36 + } + ], + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 3, + "type": "VEC3" + }, + { + "bufferView": 1, + "componentType": 5126, + "count": 3, + "type": "VEC3" + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "POSITION": 0 + }, + "material": 0 + } + ] + }, + { + "primitives": [ + { + "attributes": { + "POSITION": 1 + }, + "material": 1 + } + ] + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 0 + } + }, + "name": "Red" + }, + { + "pbrMetallicRoughness": { + "baseColorFactor": [0.5, 0.5, 0.5, 1], + "baseColorTexture": { + "index": 1 + } + }, + "name": "DarkBlue" + } + ], + "samplers": [ + { + "wrapS": 10497, + "wrapT": 10497, + "minFilter": 9987, + "magFilter": 9729 + } + ], + "textures": [ + { + "sampler": 0, + "source": 0 + }, + { + "sampler": 0, + "source": 1 + } + ], + "images": [ + { + "uri": "materials-pbr.0.png", + "name": "red2x2.png" + }, + { + "uri": "materials-pbr.1.png", + "name": "blue4x4.png" + } + ], + "nodes": [ + { + "mesh": 0, + "name": "First" + }, + { + "mesh": 1, + "name": "Second" + } + ], + "scenes": [ + { + "nodes": [0, 1] + } + ], + "scene": 0 +} diff --git a/src/Magnum/SceneTools/Test/SceneConverterTestFiles/materials-phong.mtl b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/materials-phong.mtl new file mode 100644 index 000000000..8b44e7dc7 --- /dev/null +++ b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/materials-phong.mtl @@ -0,0 +1,7 @@ +newmtl Red +Ka 0.1 0.1 0.1 +map_Kd red2x2.png + +newmtl DarkBlue +Kd 0.5 0.5 0.5 +map_Kd blue4x4.png diff --git a/src/Magnum/SceneTools/Test/SceneConverterTestFiles/materials-phong.obj b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/materials-phong.obj new file mode 100644 index 000000000..bfb51b071 --- /dev/null +++ b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/materials-phong.obj @@ -0,0 +1,15 @@ +mtllib materials-phong.mtl + +# Same as two-triangles.obj, but with material references +o First +usemtl Red +v -1 -1 0 +v 1 -1 0 +v -1 1 0 +f 1 2 3 +o Second +usemtl DarkBlue +v -1 1 0 +v 1 -1 0 +v 1 1 0 +f 4 5 6 diff --git a/src/Magnum/SceneTools/Test/SceneConverterTestFiles/materials-separate-metalness-roughness.mtl b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/materials-separate-metalness-roughness.mtl new file mode 100644 index 000000000..c4d20f06d --- /dev/null +++ b/src/Magnum/SceneTools/Test/SceneConverterTestFiles/materials-separate-metalness-roughness.mtl @@ -0,0 +1,9 @@ +newmtl Material +Kd 0.0 1.0 0.0 + +newmtl Separate metalness and roughness +# Has to be present as otherwise UfbxImporter doesn't load the textures at all +# https://github.com/ufbx/ufbx/issues/55 +Pm 0.0 +map_Pr blue4x4.png +map_Pm red2x2.png diff --git a/src/Magnum/SceneTools/sceneconverter.cpp b/src/Magnum/SceneTools/sceneconverter.cpp index a0c31e88f..70e30466e 100644 --- a/src/Magnum/SceneTools/sceneconverter.cpp +++ b/src/Magnum/SceneTools/sceneconverter.cpp @@ -33,6 +33,7 @@ #include #include /* parseNumberSequence() */ +#include "Magnum/MaterialTools/PhongToPbrMetallicRoughness.h" #include "Magnum/MeshTools/Concatenate.h" #include "Magnum/MeshTools/Reference.h" #include "Magnum/MeshTools/RemoveDuplicates.h" @@ -180,7 +181,7 @@ magnum-sceneconverter [-h|--help] [-I|--importer PLUGIN] [-C|--converter PLUGIN]... [-P|--image-converter PLUGIN]... [-M|--mesh-converter PLUGIN]... [--plugin-dir DIR] [--map] [--only-mesh-attributes N1,N2-N3…] [--remove-duplicate-vertices] - [--remove-duplicate-vertices-fuzzy EPSILON] + [--remove-duplicate-vertices-fuzzy EPSILON] [--phong-to-pbr] [-i|--importer-options key=val,key2=val2,…] [-c|--converter-options key=val,key2=val2,…]... [-p|--image-converter-options key=val,key2=val2,…]... @@ -217,6 +218,8 @@ Arguments: - `--remove-duplicate-vertices-fuzzy EPSILON` --- remove duplicate vertices using @ref MeshTools::removeDuplicatesFuzzy(const Trade::MeshData&, Float, Double) in all meshes after import +- `--phong-to-pbr` --- convert Phong materials to PBR metallic/roughness + using @ref MaterialTools::phongToPbrMetallicRoughness() - `-i`, `--importer-options key=val,key2=val2,…` --- configuration options to pass to the importer - `-c`, `--converter-options key=val,key2=val2,…` --- configuration options @@ -292,8 +295,8 @@ feature for given image dimensions, all mesh converters in the chain have to support the ConvertMesh feature. If no `-P` / `-M` is specified, the imported images / meshes are passed directly to the scene converter. -The `--remove-duplicate-vertices` operations are performed before passing them -to any converter. +The `--remove-duplicate-vertices` and `--phong-to-pbr` operations are performed +on meshes and materials before passing them to any converter. If `--concatenate-meshes` is given, all meshes of the input file are first concatenated into a single mesh using @ref MeshTools::concatenate(), with @@ -404,6 +407,7 @@ int main(int argc, char** argv) { .addOption("only-mesh-attributes").setHelp("only-mesh-attributes", "include only mesh attributes of given IDs in the output", "N1,N2-N3…") .addBooleanOption("remove-duplicate-vertices").setHelp("remove-duplicate-vertices", "remove duplicate vertices in all meshes after import") .addOption("remove-duplicate-vertices-fuzzy").setHelp("remove-duplicate-vertices-fuzzy", "remove duplicate vertices with fuzzy comparison in all meshes after import", "EPSILON") + .addBooleanOption("phong-to-pbr").setHelp("phong-to-pbr", "convert Phong materials to PBR metallic/roughness") .addOption('i', "importer-options").setHelp("importer-options", "configuration options to pass to the importer", "key=val,key2=val2,…") .addArrayOption('c', "converter-options").setHelp("converter-options", "configuration options to pass to the converter(s)", "key=val,key2=val2,…") .addArrayOption('p', "image-converter-options").setHelp("image-converter-options", "configuration options to pass to the image converter(s)", "key=val,key2=val2,…") @@ -478,8 +482,8 @@ feature for given image dimensions, all mesh converters in the chain have to support the ConvertMesh feature. If no -P / -M is specified, the imported images / meshes are passed directly to the scene converter. -The --remove-duplicate-vertices operations are performed on meshes before -passing them to any converter. +The --remove-duplicate-vertices and --phong-to-pbr operations are performed on +meshes and materials before passing them to any converter. If --concatenate-meshes is given, all meshes of the input file are first concatenated into a single mesh, with the scene hierarchy transformation baked @@ -943,6 +947,39 @@ well, the IDs reference attributes of the first mesh.)") } } + /* Operations to perform on all materials in the importer. If there are + any, materials are supplied manually to the converter from the array + below. */ + Containers::Array materials; + if(args.isSet("phong-to-pbr")) { + arrayReserve(materials, importer->materialCount()); + + for(UnsignedInt i = 0; i != importer->materialCount(); ++i) { + Containers::Optional material; + { + Trade::Implementation::Duration d{importConversionTime}; + if(!(material = importer->material(i))) { + Error{} << "Cannot import material" << i; + return 1; + } + } + + /* Phong to PBR conversion */ + if(args.isSet("phong-to-pbr")) { + if(args.isSet("verbose")) + Debug{} << "Converting material" << i << "to PBR"; + + Trade::Implementation::Duration d{conversionTime}; + /** @todo make the flags configurable as well? then the below + assert can actually fire, convert to a runtime error */ + material = MaterialTools::phongToPbrMetallicRoughness(*material, MaterialTools::PhongToPbrMetallicRoughnessFlag::DropUnconvertableAttributes); + CORRADE_INTERNAL_ASSERT(material); + } + + arrayAppend(materials, *std::move(material)); + } + } + /* Assume there's always one passed --converter option less, and the last is implicitly AnySceneConverter. All converters except the last one are expected to support ConvertMesh and the mesh is "piped" from one to the @@ -1113,6 +1150,59 @@ well, the IDs reference attributes of the first mesh.)") meshes = {}; } + /* If there are any loose materials from previous conversion steps, add + them directly, and clear the array so the next iteration (if any) + takes them from the importer instead */ + if(materials) { + /* Materials reference textures (and those reference images), thus + we need to add those first */ + { + const Trade::SceneContents materialDependencies = contents & + (Trade::SceneContent::Images1D| + Trade::SceneContent::Images2D| + Trade::SceneContent::Images3D| + Trade::SceneContent::ImageLevels| + Trade::SceneContent::Textures| + Trade::SceneContent::Names); + + Trade::Implementation::Duration d{importConversionTime}; + if(!converter->addSupportedImporterContents(*importer, materialDependencies)) { + Error{} << "Cannot add material dependencies"; + return 5; + } + + /* Ensure these are not added by addSupportedImporterContents() + again below, except for names -- those should be added as + long as they were in the contents originally. */ + contents &= ~(materialDependencies & ~Trade::SceneContent::Names); + } + + if(!(Trade::sceneContentsFor(*converter) & Trade::SceneContent::Materials)) { + Warning{} << "Ignoring" << materials.size() << "materials not supported by the converter"; + } else for(UnsignedInt i = 0; i != materials.size(); ++i) { + Trade::Implementation::Duration d{conversionTime}; + + if(!converter->add(materials[i], contents & Trade::SceneContent::Names ? importer->materialName(i) : Containers::String{})) { + Error{} << "Cannot add material" << i; + return 1; + } + } + + /* Ensure the materials are not added by + addSupportedImporterContents() below. Do this also in case the + converter actually doesn't support mesh addition, as it would + otherwise cause two warnings about the same "not supported" + thing being printed. */ + contents &= ~Trade::SceneContent::Materials; + + /* Delete the list to avoid adding them again for the next + converter (at which point they would be stale) */ + /** @todo this line is untested, needs two chained conversion steps + that each change the output to verify the old materials don't + get reused in the next step again */ + materials = {}; + } + { Trade::Implementation::Duration d{importConversionTime}; if(!converter->addSupportedImporterContents(*importer, contents)) {