Browse Source

python: expose trade.AbstractSceneConverter batch mode for files.

Just meshes for now.
next
Vladimír Vondruš 3 years ago
parent
commit
d29c5554d8
  1. 18
      doc/python/magnum.trade.rst
  2. 1
      package/ci/appveyor-desktop-gles.bat
  3. 1
      package/ci/appveyor-desktop.bat
  4. 1
      package/ci/unix-desktop-gles.sh
  5. 1
      package/ci/unix-desktop.sh
  6. 21
      src/python/magnum/test/mesh.gltf
  7. 101
      src/python/magnum/test/test_trade.py
  8. 55
      src/python/magnum/trade.cpp

18
doc/python/magnum.trade.rst

@ -513,3 +513,21 @@
all backslashes in :p:`filename` to forward slashes before passing it to all backslashes in :p:`filename` to forward slashes before passing it to
:dox:`Trade::AbstractSceneConverter::convertToFile()`, which expects :dox:`Trade::AbstractSceneConverter::convertToFile()`, which expects
forward slashes as directory separators on all platforms. forward slashes as directory separators on all platforms.
.. py:function:: magnum.trade.AbstractSceneConverter.begin_file
:raise RuntimeError: If beginning the conversion fails
.. py:function:: magnum.trade.AbstractSceneConverter.end_file
:raise AssertionError: If no conversion is in progress
:raise RuntimeError: If ending the conversion fails
.. py:property:: magnum.trade.AbstractSceneConverter.mesh_count
:raise AssertionError: If no conversion is in progress
.. py:function:: magnum.trade.AbstractSceneConverter.add
:raise AssertionError: If no conversion is in progress
:raise RuntimeError: If adding the data fails
.. py:function:: magnum.trade.AbstractSceneConverter.set_mesh_attribute_name
:raise AssertionError: If no conversion is in progress
:raise AssertionError: If :p:`attribute` is not custom

1
package/ci/appveyor-desktop-gles.bat

@ -78,6 +78,7 @@ cmake .. ^
-DMAGNUM_BUILD_STATIC=%BUILD_STATIC% ^ -DMAGNUM_BUILD_STATIC=%BUILD_STATIC% ^
-DMAGNUM_WITH_DDSIMPORTER=ON ^ -DMAGNUM_WITH_DDSIMPORTER=ON ^
-DMAGNUM_WITH_GLTFIMPORTER=ON ^ -DMAGNUM_WITH_GLTFIMPORTER=ON ^
-DMAGNUM_WITH_GLTFSCENECONVERTER=ON ^
-DMAGNUM_WITH_MESHOPTIMIZERSCENECONVERTER=ON ^ -DMAGNUM_WITH_MESHOPTIMIZERSCENECONVERTER=ON ^
-DMAGNUM_WITH_STANFORDSCENECONVERTER=ON ^ -DMAGNUM_WITH_STANFORDSCENECONVERTER=ON ^
-DMAGNUM_WITH_STBIMAGECONVERTER=ON ^ -DMAGNUM_WITH_STBIMAGECONVERTER=ON ^

1
package/ci/appveyor-desktop.bat

@ -88,6 +88,7 @@ cmake .. ^
-DMAGNUM_BUILD_STATIC=%BUILD_STATIC% ^ -DMAGNUM_BUILD_STATIC=%BUILD_STATIC% ^
-DMAGNUM_WITH_DDSIMPORTER=ON ^ -DMAGNUM_WITH_DDSIMPORTER=ON ^
-DMAGNUM_WITH_GLTFIMPORTER=ON ^ -DMAGNUM_WITH_GLTFIMPORTER=ON ^
-DMAGNUM_WITH_GLTFSCENECONVERTER=ON ^
-DMAGNUM_WITH_MESHOPTIMIZERSCENECONVERTER=%EXCEPT_MSVC2017% ^ -DMAGNUM_WITH_MESHOPTIMIZERSCENECONVERTER=%EXCEPT_MSVC2017% ^
-DMAGNUM_WITH_STANFORDSCENECONVERTER=ON ^ -DMAGNUM_WITH_STANFORDSCENECONVERTER=ON ^
-DMAGNUM_WITH_STBIMAGECONVERTER=ON ^ -DMAGNUM_WITH_STBIMAGECONVERTER=ON ^

1
package/ci/unix-desktop-gles.sh

@ -65,6 +65,7 @@ cmake .. \
-DMAGNUM_BUILD_STATIC=$BUILD_STATIC \ -DMAGNUM_BUILD_STATIC=$BUILD_STATIC \
-DMAGNUM_WITH_DDSIMPORTER=ON \ -DMAGNUM_WITH_DDSIMPORTER=ON \
-DMAGNUM_WITH_GLTFIMPORTER=ON \ -DMAGNUM_WITH_GLTFIMPORTER=ON \
-DMAGNUM_WITH_GLTFSCENECONVERTER=ON \
-DMAGNUM_WITH_MESHOPTIMIZERSCENECONVERTER=ON \ -DMAGNUM_WITH_MESHOPTIMIZERSCENECONVERTER=ON \
-DMAGNUM_WITH_STANFORDSCENECONVERTER=ON \ -DMAGNUM_WITH_STANFORDSCENECONVERTER=ON \
-DMAGNUM_WITH_STBIMAGECONVERTER=ON \ -DMAGNUM_WITH_STBIMAGECONVERTER=ON \

1
package/ci/unix-desktop.sh

@ -69,6 +69,7 @@ cmake .. \
-DMAGNUM_BUILD_STATIC=$BUILD_STATIC \ -DMAGNUM_BUILD_STATIC=$BUILD_STATIC \
-DMAGNUM_WITH_DDSIMPORTER=ON \ -DMAGNUM_WITH_DDSIMPORTER=ON \
-DMAGNUM_WITH_GLTFIMPORTER=ON \ -DMAGNUM_WITH_GLTFIMPORTER=ON \
-DMAGNUM_WITH_GLTFSCENECONVERTER=ON \
-DMAGNUM_WITH_MESHOPTIMIZERSCENECONVERTER=ON \ -DMAGNUM_WITH_MESHOPTIMIZERSCENECONVERTER=ON \
-DMAGNUM_WITH_STANFORDSCENECONVERTER=ON \ -DMAGNUM_WITH_STANFORDSCENECONVERTER=ON \
-DMAGNUM_WITH_STBIMAGECONVERTER=ON \ -DMAGNUM_WITH_STBIMAGECONVERTER=ON \

21
src/python/magnum/test/mesh.gltf

@ -39,6 +39,27 @@
"mode": 666 "mode": 666
} }
] ]
},
{
"name": "Point mesh",
"primitives": [
{
"attributes": {
"POSITION": 1
},
"mode": 0
}
]
},
{
"name": "Custom mesh attribute",
"primitives": [
{
"attributes": {
"_FOOBARTHINGY": 1
}
}
]
} }
], ],
"accessors": [ "accessors": [

101
src/python/magnum/test/test_trade.py

@ -1306,7 +1306,7 @@ class Importer(unittest.TestCase):
self.assertIsNone(importer.mesh_attribute_for_name("_CUSTOM_ATTRIBUTE")) self.assertIsNone(importer.mesh_attribute_for_name("_CUSTOM_ATTRIBUTE"))
importer.open_file(os.path.join(os.path.dirname(__file__), 'mesh.gltf')) importer.open_file(os.path.join(os.path.dirname(__file__), 'mesh.gltf'))
self.assertEqual(importer.mesh_count, 3) self.assertEqual(importer.mesh_count, 5)
self.assertEqual(importer.mesh_level_count(0), 1) self.assertEqual(importer.mesh_level_count(0), 1)
self.assertEqual(importer.mesh_name(0), 'Indexed mesh') self.assertEqual(importer.mesh_name(0), 'Indexed mesh')
self.assertEqual(importer.mesh_for_name('Indexed mesh'), 0) self.assertEqual(importer.mesh_for_name('Indexed mesh'), 0)
@ -1542,3 +1542,102 @@ class SceneConverter(unittest.TestCase):
with tempfile.TemporaryDirectory() as tmp: with tempfile.TemporaryDirectory() as tmp:
with self.assertRaisesRegex(RuntimeError, "conversion failed"): with self.assertRaisesRegex(RuntimeError, "conversion failed"):
converter.convert_to_file(mesh, os.path.join(tmp, "mesh.obj")) converter.convert_to_file(mesh, os.path.join(tmp, "mesh.obj"))
def test_batch_file(self):
importer = trade.ImporterManager().load_and_instantiate('GltfImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'mesh.gltf'))
mesh = importer.mesh(1)
converter = trade.SceneConverterManager().load_and_instantiate('StanfordSceneConverter')
with tempfile.TemporaryDirectory() as tmp:
converter.begin_file(os.path.join(tmp, "mesh.ply"))
self.assertTrue(converter.is_converting)
self.assertEqual(converter.mesh_count, 0)
self.assertEqual(converter.add(mesh), 0)
self.assertEqual(converter.mesh_count, 1)
converter.end_file()
self.assertFalse(converter.is_converting)
self.assertTrue(os.path.exists(os.path.join(tmp, "mesh.ply")))
def test_batch_file_begin_failed(self):
importer = trade.ImporterManager().load_and_instantiate('GltfImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'mesh.gltf'))
mesh = importer.mesh(1)
converter = trade.SceneConverterManager().load_and_instantiate('AnySceneConverter')
with tempfile.TemporaryDirectory() as tmp:
with self.assertRaisesRegex(RuntimeError, "beginning the conversion failed"):
converter.begin_file(os.path.join(tmp, "mesh.obj"))
def test_batch_file_end_failed(self):
importer = trade.ImporterManager().load_and_instantiate('GltfImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'mesh.gltf'))
mesh = importer.mesh(1)
converter = trade.SceneConverterManager().load_and_instantiate('StanfordSceneConverter')
with tempfile.TemporaryDirectory() as tmp:
converter.begin_file(os.path.join(tmp, "mesh.ply"))
with self.assertRaisesRegex(RuntimeError, "ending the conversion failed"):
converter.end_file()
def test_batch_add_mesh_failed(self):
importer = trade.ImporterManager().load_and_instantiate('GltfImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'mesh.gltf'))
mesh = importer.mesh('Point mesh')
converter = trade.SceneConverterManager().load_and_instantiate('StanfordSceneConverter')
with tempfile.TemporaryDirectory() as tmp:
converter.begin_file(os.path.join(tmp, "mesh.ply"))
with self.assertRaisesRegex(RuntimeError, "adding the mesh failed"):
converter.add(mesh)
def test_batch_set_mesh_attribute_name(self):
importer = trade.ImporterManager().load_and_instantiate('GltfImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'mesh.gltf'))
mesh = importer.mesh('Custom mesh attribute')
converter = trade.SceneConverterManager().load_and_instantiate('GltfSceneConverter')
with tempfile.TemporaryDirectory() as tmp:
filename = os.path.join(tmp, "mesh.gltf")
converter.begin_file(filename)
converter.set_mesh_attribute_name(importer.mesh_attribute_for_name('_FOOBARTHINGY'), '_FOOBARTHINGY')
converter.add(mesh)
converter.end_file()
with open(filename, 'r') as f:
self.assertIn('_FOOBARTHINGY', f.read())
def test_batch_set_mesh_attribute_name_not_custom(self):
converter = trade.SceneConverterManager().load_and_instantiate('GltfSceneConverter')
with tempfile.TemporaryDirectory() as tmp:
filename = os.path.join(tmp, "mesh.gltf")
converter.begin_file(filename)
with self.assertRaisesRegex(AssertionError, "not a custom attribute"):
converter.set_mesh_attribute_name(trade.MeshAttribute.POSITION, 'foo')
def test_batch_no_conversion_in_progress(self):
importer = trade.ImporterManager().load_and_instantiate('GltfImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'mesh.gltf'))
mesh = importer.mesh('Custom mesh attribute')
converter = trade.SceneConverterManager().load_and_instantiate('GltfSceneConverter')
self.assertFalse(converter.is_converting)
with self.assertRaisesRegex(AssertionError, "no conversion in progress"):
converter.end_file()
with self.assertRaisesRegex(AssertionError, "no conversion in progress"):
converter.mesh_count
with self.assertRaisesRegex(AssertionError, "no conversion in progress"):
converter.add(mesh)
with self.assertRaisesRegex(AssertionError, "no conversion in progress"):
converter.set_mesh_attribute_name(trade.MeshAttribute.CUSTOM(1), 'foobar')

55
src/python/magnum/trade.cpp

@ -1676,7 +1676,60 @@ void trade(py::module_& m) {
PyErr_SetString(PyExc_RuntimeError, "conversion failed"); PyErr_SetString(PyExc_RuntimeError, "conversion failed");
throw py::error_already_set{}; throw py::error_already_set{};
} }
}, "Convert a mesh to a file", py::arg("mesh"), py::arg("filename")); }, "Convert a mesh to a file", py::arg("mesh"), py::arg("filename"))
.def_property_readonly("is_converting", &Trade::AbstractSceneConverter::isConverting, "Whether any conversion is in progress")
.def("abort", &Trade::AbstractSceneConverter::abort, "Abort any in-progress conversion")
/** @todo begin/end (MeshOptimizer), begin/end data */
/** @todo drop std::string in favor of our own string caster */
.def("begin_file", [](Trade::AbstractSceneConverter& self, const std::string& filename) {
if(!self.beginFile(filename)) {
PyErr_SetString(PyExc_RuntimeError, "beginning the conversion failed");
throw py::error_already_set{};
}
}, "Begin converting a scene to a file", py::arg("filename"))
.def("end_file", [](Trade::AbstractSceneConverter& self) {
/** @todo this doesn't catch a mismatch (e.g., when beginData() was
called instead */
if(!self.isConverting()) {
PyErr_SetString(PyExc_AssertionError, "no conversion in progress");
throw py::error_already_set{};
}
if(!self.endFile()) {
PyErr_SetString(PyExc_RuntimeError, "ending the conversion failed");
throw py::error_already_set{};
}
}, "End converting a scene to a file")
.def_property_readonly("mesh_count", [](Trade::AbstractSceneConverter& self) {
if(!self.isConverting()) {
PyErr_SetString(PyExc_AssertionError, "no conversion in progress");
throw py::error_already_set{};
}
return self.meshCount();
}, "Count of added meshes")
/** @todo drop std::string in favor of our own string caster */
.def("add", [](Trade::AbstractSceneConverter& self, const Trade::MeshData& mesh, const std::string& name) {
if(!self.isConverting()) {
PyErr_SetString(PyExc_AssertionError, "no conversion in progress");
throw py::error_already_set{};
}
if(const Containers::Optional<UnsignedInt> out = self.add(mesh, name))
return *out;
PyErr_SetString(PyExc_RuntimeError, "adding the mesh failed");
throw py::error_already_set{};
}, "Add a mesh", py::arg("mesh"), py::arg("name") = std::string{})
/** @todo mesh levels */
.def("set_mesh_attribute_name", [](Trade::AbstractSceneConverter& self, const Trade::MeshAttribute attribute, const std::string& name) {
if(!Trade::isMeshAttributeCustom(attribute)) {
PyErr_SetString(PyExc_AssertionError, "not a custom attribute");
throw py::error_already_set{};
}
if(!self.isConverting()) {
PyErr_SetString(PyExc_AssertionError, "no conversion in progress");
throw py::error_already_set{};
}
self.setMeshAttributeName(attribute, name);
}, "Set name of a custom mesh attribute", py::arg("attribute"), py::arg("name"));
corrade::plugin(abstractSceneConverter); corrade::plugin(abstractSceneConverter);
py::class_<PluginManager::Manager<Trade::AbstractSceneConverter>, PluginManager::AbstractManager> sceneConverterManager{m, "SceneConverterManager", "Manager for scene converter plugins"}; py::class_<PluginManager::Manager<Trade::AbstractSceneConverter>, PluginManager::AbstractManager> sceneConverterManager{m, "SceneConverterManager", "Manager for scene converter plugins"};

Loading…
Cancel
Save