Browse Source

python: expose trade.AbstractConverter.add*_importer_contents().

next
Vladimír Vondruš 3 years ago
parent
commit
e319f098eb
  1. 14
      doc/python/magnum.trade.rst
  2. 89
      src/python/magnum/test/test_trade.py
  3. 89
      src/python/magnum/test/two-meshes.gltf
  4. 56
      src/python/magnum/trade.cpp

14
doc/python/magnum.trade.rst

@ -476,6 +476,12 @@
:ref:`AbstractSceneConverter.manager`, ensuring the manager is not deleted
before the plugin instances are.
.. py:enum:: magnum.trade.SceneContents
The equivalent to C++ :dox:`Trade::sceneContentsFor()` is creating an enum
value using a ``FOR()`` named constructor, passing either an
:ref:`AbstractSceneConverter` or an opened :ref:`AbstractImporter` to it.
.. TODO couldn't the plugin_interface etc. docs be parsed from pybind's docs?
repeating them for every plugin is annoying
@ -531,3 +537,11 @@
.. 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
.. py:function:: magnum.trade.AbstractSceneConverter.add_importer_contents
:raise AssertionError: If no conversion is in progress
:raise RuntimeError: If adding the importer contents fails
.. py:function:: magnum.trade.AbstractSceneConverter.add_supported_importer_contents
:raise AssertionError: If no conversion is in progress
:raise RuntimeError: If adding the importer contents fails

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

@ -1457,6 +1457,27 @@ class ImageConverter(unittest.TestCase):
converter.convert_to_file(image, os.path.join(tmp, "image.hdr"))
class SceneConverter(unittest.TestCase):
def test_scenecontents_for_importer(self):
# Silly, yes, but don't want to enable StanfordImporter just for this
# test case
importer = trade.ImporterManager().load_and_instantiate('StbImageImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'rgb.png'))
self.assertEqual(trade.SceneContents.FOR(importer), trade.SceneContents.IMAGES2D|trade.SceneContents.NAMES)
def test_scenecontents_for_importer_not_opened(self):
# Silly, yes, but don't want to enable StanfordImporter just for this
# test case
importer = trade.ImporterManager().load_and_instantiate('StbImageImporter')
with self.assertRaisesRegex(AssertionError, "no file opened"):
trade.SceneContents.FOR(importer)
def test_scenecontents_for_converter(self):
converter = trade.SceneConverterManager().load_and_instantiate('StanfordSceneConverter')
self.assertEqual(trade.SceneContents.FOR(converter), trade.SceneContents.MESHES|trade.SceneContents.NAMES)
def test(self):
converter = trade.SceneConverterManager().load_and_instantiate('StanfordSceneConverter')
self.assertEqual(converter.features, trade.SceneConverterFeatures.CONVERT_MESH_TO_FILE|trade.SceneConverterFeatures.CONVERT_MESH_TO_DATA)
@ -1625,6 +1646,70 @@ class SceneConverter(unittest.TestCase):
with self.assertRaisesRegex(AssertionError, "not a custom attribute"):
converter.set_mesh_attribute_name(trade.MeshAttribute.POSITION, 'foo')
def test_batch_add_importer_contents(self):
importer = trade.ImporterManager().load_and_instantiate('GltfImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'two-meshes.gltf'))
converter = trade.SceneConverterManager().load_and_instantiate('GltfSceneConverter')
with tempfile.TemporaryDirectory() as tmp:
filename = os.path.join(tmp, "two-meshes.gltf")
converter.begin_file(filename)
self.assertEqual(converter.mesh_count, 0)
# Nothing like that in the file
converter.add_importer_contents(importer, trade.SceneContents.SCENES|trade.SceneContents.CAMERAS)
self.assertEqual(converter.mesh_count, 0)
converter.add_importer_contents(importer)
self.assertEqual(converter.mesh_count, 2)
def test_batch_add_importer_contents_failed(self):
importer = trade.ImporterManager().load_and_instantiate('GltfImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'two-meshes.gltf'))
converter = trade.SceneConverterManager().load_and_instantiate('StanfordSceneConverter')
with tempfile.TemporaryDirectory() as tmp:
filename = os.path.join(tmp, "two-meshes.gltf")
converter.begin_file(filename)
with self.assertRaisesRegex(RuntimeError, "adding importer contents failed"):
converter.add_importer_contents(importer)
def test_batch_add_supported_importer_contents(self):
importer = trade.ImporterManager().load_and_instantiate('GltfImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'scene.gltf'))
converter = trade.SceneConverterManager().load_and_instantiate('StanfordSceneConverter')
with tempfile.TemporaryDirectory() as tmp:
filename = os.path.join(tmp, "two-meshes.gltf")
converter.begin_file(filename)
self.assertEqual(converter.mesh_count, 0)
# Nothing like that in the file
converter.add_supported_importer_contents(importer, trade.SceneContents.MESHES)
self.assertEqual(converter.mesh_count, 0)
# It contains cameras, nodes and scenes, none of which is supported
# by the converter
converter.add_supported_importer_contents(importer)
self.assertEqual(converter.mesh_count, 0)
def test_batch_add_supported_importer_contents_failed(self):
importer = trade.ImporterManager().load_and_instantiate('GltfImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'two-meshes.gltf'))
converter = trade.SceneConverterManager().load_and_instantiate('StanfordSceneConverter')
with tempfile.TemporaryDirectory() as tmp:
filename = os.path.join(tmp, "two-meshes.gltf")
converter.begin_file(filename)
with self.assertRaisesRegex(RuntimeError, "adding importer contents failed"):
converter.add_supported_importer_contents(importer)
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'))
@ -1641,3 +1726,7 @@ class SceneConverter(unittest.TestCase):
converter.add(mesh)
with self.assertRaisesRegex(AssertionError, "no conversion in progress"):
converter.set_mesh_attribute_name(trade.MeshAttribute.CUSTOM(1), 'foobar')
with self.assertRaisesRegex(AssertionError, "no conversion in progress"):
converter.add_importer_contents(importer)
with self.assertRaisesRegex(AssertionError, "no conversion in progress"):
converter.add_supported_importer_contents(importer)

89
src/python/magnum/test/two-meshes.gltf

@ -0,0 +1,89 @@
{
"asset": {
"version": "2.0",
"note": "same as meshes.gltf, just with no errors, no custom attribs, no attribs that aren't valid glTF and no skinning attribs either"
},
"meshes": [
{
"name": "Indexed mesh",
"primitives": [
{
"attributes": {
"POSITION": 1,
"TEXCOORD_0": 2,
"COLOR": 3,
"TEXCOORD_1": 2
},
"indices": 0
}
]
},
{
"name": "Non-indexed mesh",
"primitives": [
{
"attributes": {
"POSITION": 1
}
}
]
}
],
"accessors": [
{
"bufferView": 0,
"byteOffset": 2,
"componentType": 5123,
"count": 3,
"type": "SCALAR"
},
{
"bufferView": 1,
"componentType": 5126,
"count": 3,
"type": "VEC3"
},
{
"bufferView": 1,
"byteOffset": 12,
"componentType": 5126,
"count": 3,
"type": "VEC2"
},
{
"bufferView": 1,
"byteOffset": 20,
"componentType": 5121,
"normalized": true,
"count": 3,
"type": "VEC3"
},
{
"bufferView": 1,
"byteOffset": 24,
"componentType": 5125,
"count": 3,
"type": "SCALAR"
}
],
"bufferViews": [
{
"buffer": 0,
"byteOffset": 0,
"byteLength": 8
},
{
"buffer": 0,
"byteOffset": 8,
"byteLength": 84,
"byteStride": 28
}
],
"buffers": [
{
"byteLength": 92,
"uri": "mesh.bin"
}
]
}

56
src/python/magnum/trade.cpp

@ -1630,6 +1630,36 @@ void trade(py::module_& m) {
.value("NONE", Trade::SceneConverterFlag{});
corrade::enumOperators(sceneConverterFlags);
py::enum_<Trade::SceneContent> sceneContents{m, "SceneContents", "Scene contents"};
sceneContents
.value("SCENES", Trade::SceneContent::Scenes)
.value("ANIMATIONS", Trade::SceneContent::Animations)
.value("LIGHTS", Trade::SceneContent::Lights)
.value("CAMERAS", Trade::SceneContent::Cameras)
.value("SKINS2D", Trade::SceneContent::Skins2D)
.value("SKINS3D", Trade::SceneContent::Skins3D)
.value("MESHES", Trade::SceneContent::Meshes)
.value("MATERIALS", Trade::SceneContent::Materials)
.value("TEXTURES", Trade::SceneContent::Textures)
.value("IMAGES1D", Trade::SceneContent::Images1D)
.value("IMAGES2D", Trade::SceneContent::Images2D)
.value("IMAGES3D", Trade::SceneContent::Images3D)
.value("MESH_LEVELS", Trade::SceneContent::MeshLevels)
.value("IMAGE_LEVELS", Trade::SceneContent::ImageLevels)
.value("NAMES", Trade::SceneContent::Names)
.value("ALL", Trade::SceneContent(Containers::enumCastUnderlyingType(~Trade::SceneContent{})))
.def("FOR", [](Trade::AbstractImporter& importer) {
if(!importer.isOpened()) {
PyErr_SetString(PyExc_AssertionError, "no file opened");
throw py::error_already_set{};
}
return Trade::SceneContent(Containers::enumCastUnderlyingType(Trade::sceneContentsFor(importer)));
})
.def("FOR", [](Trade::AbstractSceneConverter& converter) {
return Trade::SceneContent(Containers::enumCastUnderlyingType(Trade::sceneContentsFor(converter)));
});
corrade::enumOperators(sceneContents);
py::class_<Trade::AbstractSceneConverter, PluginManager::PyPluginHolder<Trade::AbstractSceneConverter>, PluginManager::AbstractPlugin> abstractSceneConverter{m, "AbstractSceneConverter", "Interface for scene converter plugins"};
abstractSceneConverter
.def_property_readonly("features", [](Trade::AbstractSceneConverter& self) {
@ -1729,7 +1759,31 @@ void trade(py::module_& m) {
throw py::error_already_set{};
}
self.setMeshAttributeName(attribute, name);
}, "Set name of a custom mesh attribute", py::arg("attribute"), py::arg("name"));
}, "Set name of a custom mesh attribute", py::arg("attribute"), py::arg("name"))
.def("add_importer_contents", [](Trade::AbstractSceneConverter& self, Trade::AbstractImporter& importer, Trade::SceneContent contents) {
if(!self.isConverting()) {
PyErr_SetString(PyExc_AssertionError, "no conversion in progress");
throw py::error_already_set{};
}
/** @todo check if contents present in the file are supported? or
make that a runtime failure in Magnum for easier use? */
if(!self.addImporterContents(importer, contents)) {
PyErr_SetString(PyExc_RuntimeError, "adding importer contents failed");
throw py::error_already_set{};
}
}, "Add importer contents", py::arg("importer"), py::arg("contents") = Trade::SceneContent(Containers::enumCastUnderlyingType(~Trade::SceneContent{})))
.def("add_supported_importer_contents", [](Trade::AbstractSceneConverter& self, Trade::AbstractImporter& importer, Trade::SceneContent contents) {
if(!self.isConverting()) {
PyErr_SetString(PyExc_AssertionError, "no conversion in progress");
throw py::error_already_set{};
}
/** @todo check if contents present in the file are supported? or
make that a runtime failure in Magnum for easier use? */
if(!self.addSupportedImporterContents(importer, contents)) {
PyErr_SetString(PyExc_RuntimeError, "adding importer contents failed");
throw py::error_already_set{};
}
}, "Add supported importer contents", py::arg("importer"), py::arg("contents") = Trade::SceneContent(Containers::enumCastUnderlyingType(~Trade::SceneContent{})));
corrade::plugin(abstractSceneConverter);
py::class_<PluginManager::Manager<Trade::AbstractSceneConverter>, PluginManager::AbstractManager> sceneConverterManager{m, "SceneConverterManager", "Manager for scene converter plugins"};

Loading…
Cancel
Save