diff --git a/doc/python/magnum.trade.rst b/doc/python/magnum.trade.rst index 3eef5d3..d5dba1c 100644 --- a/doc/python/magnum.trade.rst +++ b/doc/python/magnum.trade.rst @@ -194,3 +194,31 @@ .. py:function:: magnum.trade.AbstractImageConverter.convert_to_file :raise RuntimeError: If image conversion fails + +.. py:class:: magnum.trade.SceneConverterManager + :summary: Manager for :ref:`AbstractSceneConverter` plugin instances + + Each plugin returned by :ref:`instantiate()` or :ref:`load_and_instantiate()` + references its owning :ref:`SceneConverterManager` through + :ref:`AbstractSceneConverter.manager`, ensuring the manager is not deleted + before the plugin instances are. + +.. py:class:: magnum.trade.AbstractSceneConverter + + Similarly to C++, image converter plugins are loaded through + :ref:`SceneConverterManager`: + + .. + >>> from magnum import trade + + .. code:: py + + >>> manager = trade.SceneConverterManager() + >>> converter = manager.load_and_instantiate('StanfordSceneConverter') + + Unlike C++, errors in both API usage and file parsing are reported by + raising an exception. See particular function documentation for detailed + behavior. + +.. py:function:: magnum.trade.AbstractSceneConverter.convert_to_file + :raise RuntimeError: If scene conversion fails diff --git a/doc/python/pages/changelog.rst b/doc/python/pages/changelog.rst index 9c6cb8a..09bcea2 100644 --- a/doc/python/pages/changelog.rst +++ b/doc/python/pages/changelog.rst @@ -90,7 +90,8 @@ Changelog :ref:`platform.glfw.Application.exit_event` - Exposed :ref:`platform.glfw.Application.swap_interval` and :ref:`platform.glfw.Application.main_loop_iteration` -- Exposed a basic interface of :ref:`trade.AbstractImageConverter` +- Exposed a basic interface of :ref:`trade.AbstractImageConverter` and + :ref:`trade.AbstractSceneConverter` - Exposed :ref:`Color3.red()` and other convenience constructors (see :gh:`mosra/magnum-bindings#12`) - Fixed issues with an in-source build (see :gh:`mosra/magnum-bindings#13`) diff --git a/package/ci/appveyor-desktop-gles.bat b/package/ci/appveyor-desktop-gles.bat index 5cbf8ab..223133b 100644 --- a/package/ci/appveyor-desktop-gles.bat +++ b/package/ci/appveyor-desktop-gles.bat @@ -61,6 +61,7 @@ cmake .. ^ -DWITH_GLFWAPPLICATION=OFF ^ -DWITH_WINDOWLESSWGLAPPLICATION=ON ^ -DWITH_ANYIMAGEIMPORTER=ON ^ + -DWITH_ANYSCENECONVERTER=ON ^ -G Ninja || exit /b cmake --build . || exit /b cmake --build . --target install || exit /b @@ -75,6 +76,7 @@ cmake .. ^ -DCMAKE_INSTALL_PREFIX=%APPVEYOR_BUILD_FOLDER%/deps ^ -DBUILD_STATIC=%BUILD_STATIC% ^ -DWITH_DDSIMPORTER=ON ^ + -DWITH_STANFORDSCENECONVERTER=ON ^ -DWITH_STBIMAGECONVERTER=ON ^ -DWITH_STBIMAGEIMPORTER=ON ^ -DWITH_CGLTFIMPORTER=ON ^ diff --git a/package/ci/appveyor-desktop.bat b/package/ci/appveyor-desktop.bat index afc6493..92db445 100644 --- a/package/ci/appveyor-desktop.bat +++ b/package/ci/appveyor-desktop.bat @@ -68,6 +68,7 @@ cmake .. ^ -DWITH_GLFWAPPLICATION=ON ^ -DWITH_WINDOWLESSWGLAPPLICATION=ON ^ -DWITH_ANYIMAGEIMPORTER=ON ^ + -DWITH_ANYSCENECONVERTER=ON ^ %COMPILER_EXTRA% -G Ninja || exit /b cmake --build . || exit /b cmake --build . --target install || exit /b @@ -82,6 +83,7 @@ cmake .. ^ -DCMAKE_INSTALL_PREFIX=%APPVEYOR_BUILD_FOLDER%/deps ^ -DBUILD_STATIC=%BUILD_STATIC% ^ -DWITH_DDSIMPORTER=ON ^ + -DWITH_STANFORDSCENECONVERTER=ON ^ -DWITH_STBIMAGECONVERTER=ON ^ -DWITH_STBIMAGEIMPORTER=ON ^ -DWITH_CGLTFIMPORTER=ON ^ diff --git a/package/ci/unix-desktop-gles.sh b/package/ci/unix-desktop-gles.sh index 22209a2..a6bd398 100755 --- a/package/ci/unix-desktop-gles.sh +++ b/package/ci/unix-desktop-gles.sh @@ -44,6 +44,7 @@ cmake .. \ -DWITH_VK=OFF \ -DWITH_WINDOWLESSEGLAPPLICATION=ON \ -DWITH_ANYIMAGEIMPORTER=ON \ + -DWITH_ANYSCENECONVERTER=ON \ -G Ninja ninja install cd ../.. @@ -54,10 +55,15 @@ cd magnum-plugins mkdir build && cd build cmake .. \ -DCMAKE_INSTALL_PREFIX=$HOME/deps \ - -DCMAKE_INSTALL_RPATH=$HOME/deps/lib \ -DCMAKE_BUILD_TYPE=Release \ + `# StanfordSceneConverter uses MeshTools which rely on GL which looks` \ + `# for OpenGLES. And the RPATH entry needs to be there as well,` \ + `# otherwise it won't load.` \ + -DCMAKE_PREFIX_PATH=$HOME/swiftshader \ + -DCMAKE_INSTALL_RPATH="$HOME/deps/lib;$HOME/swiftshader/lib" \ -DBUILD_STATIC=$BUILD_STATIC \ -DWITH_DDSIMPORTER=ON \ + -DWITH_STANFORDSCENECONVERTER=ON \ -DWITH_STBIMAGECONVERTER=ON \ -DWITH_STBIMAGEIMPORTER=ON \ -DWITH_CGLTFIMPORTER=ON \ diff --git a/package/ci/unix-desktop.sh b/package/ci/unix-desktop.sh index c33f384..b26f584 100755 --- a/package/ci/unix-desktop.sh +++ b/package/ci/unix-desktop.sh @@ -45,6 +45,7 @@ cmake .. \ -DWITH_SDL2APPLICATION=ON \ -DWITH_WINDOWLESS${PLATFORM_GL_API}APPLICATION=ON \ -DWITH_ANYIMAGEIMPORTER=ON \ + -DWITH_ANYSCENECONVERTER=ON \ -G Ninja # In case of a static build there's no way for the test to know the plugin @@ -66,6 +67,7 @@ cmake .. \ -DCMAKE_BUILD_TYPE=Release \ -DBUILD_STATIC=$BUILD_STATIC \ -DWITH_DDSIMPORTER=ON \ + -DWITH_STANFORDSCENECONVERTER=ON \ -DWITH_STBIMAGECONVERTER=ON \ -DWITH_STBIMAGEIMPORTER=ON \ -DWITH_CGLTFIMPORTER=ON \ diff --git a/src/python/magnum/test/test_trade.py b/src/python/magnum/test/test_trade.py index 10fe537..c0d2231 100644 --- a/src/python/magnum/test/test_trade.py +++ b/src/python/magnum/test/test_trade.py @@ -289,3 +289,26 @@ class ImageConverter(unittest.TestCase): with tempfile.TemporaryDirectory() as tmp: with self.assertRaisesRegex(RuntimeError, "conversion failed"): converter.convert_to_file(image, os.path.join(tmp, "image.hdr")) + +class SceneConverter(unittest.TestCase): + def test_mesh(self): + importer = trade.ImporterManager().load_and_instantiate('CgltfImporter') + importer.open_file(os.path.join(os.path.dirname(__file__), 'mesh.glb')) + mesh = importer.mesh(0) + + converter = trade.SceneConverterManager().load_and_instantiate('StanfordSceneConverter') + + with tempfile.TemporaryDirectory() as tmp: + converter.convert_to_file(mesh, os.path.join(tmp, "mesh.ply")) + self.assertTrue(os.path.exists(os.path.join(tmp, "mesh.ply"))) + + def test_mesh_failed(self): + importer = trade.ImporterManager().load_and_instantiate('CgltfImporter') + importer.open_file(os.path.join(os.path.dirname(__file__), 'mesh.glb')) + mesh = importer.mesh(0) + + converter = trade.SceneConverterManager().load_and_instantiate('AnySceneConverter') + + with tempfile.TemporaryDirectory() as tmp: + with self.assertRaisesRegex(RuntimeError, "conversion failed"): + converter.convert_to_file(mesh, os.path.join(tmp, "mesh.obj")) diff --git a/src/python/magnum/trade.cpp b/src/python/magnum/trade.cpp index 696e4e4..32b2dd8 100644 --- a/src/python/magnum/trade.cpp +++ b/src/python/magnum/trade.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -253,6 +254,17 @@ template void checkResult(Trade::AbstractSceneConverter& self, const T& mesh, const std::string& filename) { + /** @todo log redirection -- but we'd need assertions to not be part of + that so when it dies, the user can still see why */ + bool out = (self.*f)(mesh, filename); + if(!out) { + PyErr_SetString(PyExc_RuntimeError, "conversion failed"); + throw py::error_already_set{}; + } +} + } void trade(py::module_& m) { @@ -341,6 +353,15 @@ void trade(py::module_& m) { py::class_, PluginManager::AbstractManager> imageConverterManager{m, "ImageConverterManager", "Manager for image converter plugins"}; corrade::manager(imageConverterManager); + + /* Scene converter */ + py::class_> abstractSceneConverter{m, "AbstractSceneConverter", "Interface for scene converter plugins"}; + abstractSceneConverter + .def("convert_to_file", checkResult, "Convert a mesh to a file", py::arg("mesh"), py::arg("filename")); + corrade::plugin(abstractSceneConverter); + + py::class_, PluginManager::AbstractManager> sceneConverterManager{m, "SceneConverterManager", "Manager for scene converter plugins"}; + corrade::manager(sceneConverterManager); } }