Browse Source

python: adapt to Magnum changes re MeshData morph target support.

next
Vladimír Vondruš 3 years ago
parent
commit
9950b9fca4
  1. 18
      doc/python/magnum.meshtools.rst
  2. 28
      doc/python/magnum.trade.rst
  3. 78
      src/python/magnum/meshtools.cpp
  4. 32
      src/python/magnum/test/test_meshtools.py
  5. 34
      src/python/magnum/test/test_trade.py
  6. 89
      src/python/magnum/trade.cpp

18
doc/python/magnum.meshtools.rst

@ -49,38 +49,44 @@
.. py:function:: magnum.meshtools.transform2d
:raise KeyError: If :p:`mesh` doesn't have
:ref:`trade.MeshAttribute.POSITION` of index :p:`id`
:ref:`trade.MeshAttribute.POSITION` of index :p:`id` (and in morph
target :p:`morph_target_id` if not :py:`-1`)
:raise AssertionError: If :ref:`trade.MeshAttribute.POSITION` are not 2D
.. py:function:: magnum.meshtools.transform2d_in_place
:raise AssertionError: If :p:`mesh` vertex data aren't
:ref:`trade.DataFlags.MUTABLE`
:raise KeyError: If :p:`mesh` doesn't have
:ref:`trade.MeshAttribute.POSITION` of index :p:`id`
:ref:`trade.MeshAttribute.POSITION` of index :p:`id` (and in morph
target :p:`morph_target_id` if not :py:`-1`)
:raise AssertionError: If :ref:`trade.MeshAttribute.POSITION` are not
:ref:`VertexFormat.VECTOR2`
.. py:function:: magnum.meshtools.transform3d
:raise KeyError: If :p:`mesh` doesn't have
:ref:`trade.MeshAttribute.POSITION` of index :p:`id`
:ref:`trade.MeshAttribute.POSITION` of index :p:`id` (and in morph
target :p:`morph_target_id` if not :py:`-1`)
:raise AssertionError: If :ref:`trade.MeshAttribute.POSITION` are not 3D
.. py:function:: magnum.meshtools.transform3d_in_place
:raise AssertionError: If :p:`mesh` vertex data aren't
:ref:`trade.DataFlags.MUTABLE`
:raise KeyError: If :p:`mesh` doesn't have
:ref:`trade.MeshAttribute.POSITION` of index :p:`id`
:ref:`trade.MeshAttribute.POSITION` of index :p:`id` (and in morph
target :p:`morph_target_id` if not :py:`-1`)
:raise AssertionError: If :ref:`trade.MeshAttribute.POSITION` are not
:ref:`VertexFormat.VECTOR3`
.. py:function:: magnum.meshtools.transform_texture_coordinates2d
:raise KeyError: If :p:`mesh` doesn't have
:ref:`trade.MeshAttribute.TEXTURE_COORDINATES` of index :p:`id`
:ref:`trade.MeshAttribute.TEXTURE_COORDINATES` of index :p:`id` (and in
morph target :p:`morph_target_id` if not :py:`-1`)
.. py:function:: magnum.meshtools.transform_texture_coordinates2d_in_place
:raise AssertionError: If :p:`mesh` vertex data aren't
:ref:`trade.DataFlags.MUTABLE`
:raise KeyError: If :p:`mesh` doesn't have
:ref:`trade.MeshAttribute.TEXTURE_COORDINATES` of index :p:`id`
:ref:`trade.MeshAttribute.TEXTURE_COORDINATES` of index :p:`id` (and in
morph target :p:`morph_target_id` if not :py:`-1`)
:raise AssertionError: If :ref:`trade.MeshAttribute.TEXTURE_COORDINATES`
are not :ref:`VertexFormat.VECTOR2`

28
doc/python/magnum.trade.rst

@ -209,9 +209,9 @@
.. py:function:: magnum.trade.MeshData.attribute_id(self, id: int)
:raise IndexError: If :p:`id` is negative or not less than
:ref:`attribute_count()`
.. py:function:: magnum.trade.MeshData.attribute_id(self, name: magnum.trade.MeshAttribute, id: int)
.. py:function:: magnum.trade.MeshData.attribute_id(self, name: magnum.trade.MeshAttribute, id: int, morph_target_id: int)
:raise KeyError: If :p:`id` is negative or not less than
:ref:`attribute_count()` for :p:`name`
:ref:`attribute_count()` for :p:`name` and :p:`morph_target_id`
Compared to the C++ API, there's no
:dox:`Trade::MeshData::findAttributeId()`, the desired workflow is instead
@ -220,41 +220,41 @@
.. py:function:: magnum.trade.MeshData.attribute_format(self, id: int)
:raise IndexError: If :p:`id` is negative or not less than
:ref:`attribute_count()`
.. py:function:: magnum.trade.MeshData.attribute_format(self, name: magnum.trade.MeshAttribute, id: int)
.. py:function:: magnum.trade.MeshData.attribute_format(self, name: magnum.trade.MeshAttribute, id: int, morph_target_id: int)
:raise KeyError: If :p:`id` is negative or not less than
:ref:`attribute_count()` for :p:`name`
:ref:`attribute_count()` for :p:`name` and :p:`morph_target_id`
.. py:function:: magnum.trade.MeshData.attribute_offset(self, id: int)
:raise IndexError: If :p:`id` is negative or not less than
:ref:`attribute_count()`
.. py:function:: magnum.trade.MeshData.attribute_offset(self, name: magnum.trade.MeshAttribute, id: int)
.. py:function:: magnum.trade.MeshData.attribute_offset(self, name: magnum.trade.MeshAttribute, id: int, morph_target_id: int)
:raise KeyError: If :p:`id` is negative or not less than
:ref:`attribute_count()` for :p:`name`
:ref:`attribute_count()` for :p:`name` and :p:`morph_target_id`
.. py:function:: magnum.trade.MeshData.attribute_stride(self, id: int)
:raise IndexError: If :p:`id` is negative or not less than
:ref:`attribute_count()`
.. py:function:: magnum.trade.MeshData.attribute_stride(self, name: magnum.trade.MeshAttribute, id: int)
.. py:function:: magnum.trade.MeshData.attribute_stride(self, name: magnum.trade.MeshAttribute, id: int, morph_target_id: int)
:raise KeyError: If :p:`id` is negative or not less than
:ref:`attribute_count()` for :p:`name`
:ref:`attribute_count()` for :p:`name` and :p:`morph_target_id`
.. py:function:: magnum.trade.MeshData.attribute_array_size(self, id: int)
:raise IndexError: If :p:`id` is negative or not less than
:ref:`attribute_count()`
.. py:function:: magnum.trade.MeshData.attribute_array_size(self, name: magnum.trade.MeshAttribute, id: int)
.. py:function:: magnum.trade.MeshData.attribute_array_size(self, name: magnum.trade.MeshAttribute, id: int, morph_target_id: int)
:raise KeyError: If :p:`id` is negative or not less than
:ref:`attribute_count()` for :p:`name`
:ref:`attribute_count()` for :p:`name` and :p:`morph_target_id`
.. py:function:: magnum.trade.MeshData.attribute(self, id: int)
:raise IndexError: If :p:`id` is negative or not less than
:ref:`attribute_count()`
.. py:function:: magnum.trade.MeshData.attribute(self, name: magnum.trade.MeshAttribute, id: int)
.. py:function:: magnum.trade.MeshData.attribute(self, name: magnum.trade.MeshAttribute, id: int, morph_target_id: int)
:raise KeyError: If :p:`id` is negative or not less than
:ref:`attribute_count()` for :p:`name`
:ref:`attribute_count()` for :p:`name` and :p:`morph_target_id`
.. py:function:: magnum.trade.MeshData.mutable_attribute(self, id: int)
:raise IndexError: If :p:`id` is negative or not less than
:ref:`attribute_count()`
:raise AttributeError: If :ref:`vertex_data_flags` doesn't contain
:ref:`DataFlags.MUTABLE`
.. py:function:: magnum.trade.MeshData.mutable_attribute(self, name: magnum.trade.MeshAttribute, id: int)
.. py:function:: magnum.trade.MeshData.mutable_attribute(self, name: magnum.trade.MeshAttribute, id: int, morph_target_id: int)
:raise KeyError: If :p:`id` is negative or not less than
:ref:`attribute_count()` for :p:`name`
:ref:`attribute_count()` for :p:`name` and :p:`morph_target_id`
:raise AttributeError: If :ref:`vertex_data_flags` doesn't contain
:ref:`DataFlags.MUTABLE`

78
src/python/magnum/meshtools.cpp

@ -187,8 +187,8 @@ void meshtools(py::module_& m) {
#endif
py::arg("float_epsilon") = Math::TypeTraits<Float>::epsilon(),
py::arg("double_epsilon") = Math::TypeTraits<Double>::epsilon())
.def("transform2d", [](const Trade::MeshData& mesh, const Matrix3& transformation, UnsignedInt id, MeshTools::InterleaveFlag flags) {
const Containers::Optional<UnsignedInt> positionAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Position, id);
.def("transform2d", [](const Trade::MeshData& mesh, const Matrix3& transformation, UnsignedInt id, Int morphTargetId, MeshTools::InterleaveFlag flags) {
const Containers::Optional<UnsignedInt> positionAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Position, id, morphTargetId);
if(!positionAttributeId) {
PyErr_SetString(PyExc_KeyError, "position attribute not found");
throw py::error_already_set{};
@ -200,15 +200,19 @@ void meshtools(py::module_& m) {
/** @todo check that the positions aren't impl-specific once
it's possible to test */
return MeshTools::transform2D(mesh, transformation, id, flags);
}, "Transform 2D positions in a mesh data", py::arg("mesh"), py::arg("transformation"), py::arg("id") = 0, py::arg("flags") = MeshTools::InterleaveFlag::PreserveInterleavedAttributes)
.def("transform2d_in_place", [](Trade::MeshData& mesh, const Matrix3& transformation, UnsignedInt id) {
return MeshTools::transform2D(mesh, transformation, id, morphTargetId, flags);
}, "Transform 2D positions in a mesh data", py::arg("mesh"), py::arg("transformation"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
py::kw_only{}, /* new in pybind11 2.6 */
#endif
py::arg("id") = 0, py::arg("morph_target_id") = -1, py::arg("flags") = MeshTools::InterleaveFlag::PreserveInterleavedAttributes)
.def("transform2d_in_place", [](Trade::MeshData& mesh, const Matrix3& transformation, UnsignedInt id, Int morphTargetId) {
if(!(mesh.vertexDataFlags() & Trade::DataFlag::Mutable)) {
PyErr_SetString(PyExc_AssertionError, "vertex data not mutable");
throw py::error_already_set{};
}
const Containers::Optional<UnsignedInt> positionAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Position, id);
const Containers::Optional<UnsignedInt> positionAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Position, id, morphTargetId);
if(!positionAttributeId) {
PyErr_SetString(PyExc_KeyError, "position attribute not found");
throw py::error_already_set{};
@ -218,10 +222,14 @@ void meshtools(py::module_& m) {
throw py::error_already_set{};
}
MeshTools::transform2DInPlace(mesh, transformation, id);
}, "Transform 2D positions in a mesh data in-place", py::arg("mesh"), py::arg("transformation"), py::arg("id") = 0)
.def("transform3d", [](const Trade::MeshData& mesh, const Matrix4& transformation, UnsignedInt id, MeshTools::InterleaveFlag flags) {
const Containers::Optional<UnsignedInt> positionAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Position, id);
MeshTools::transform2DInPlace(mesh, transformation, id, morphTargetId);
}, "Transform 2D positions in a mesh data in-place", py::arg("mesh"), py::arg("transformation"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
py::kw_only{}, /* new in pybind11 2.6 */
#endif
py::arg("id") = 0, py::arg("morph_target_id") = -1)
.def("transform3d", [](const Trade::MeshData& mesh, const Matrix4& transformation, UnsignedInt id, Int morphTargetId, MeshTools::InterleaveFlag flags) {
const Containers::Optional<UnsignedInt> positionAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Position, id, morphTargetId);
if(!positionAttributeId) {
PyErr_SetString(PyExc_KeyError, "position attribute not found");
throw py::error_already_set{};
@ -233,15 +241,19 @@ void meshtools(py::module_& m) {
/** @todo check that the positions, normals, ... aren't
impl-specific once it's possible to test */
return MeshTools::transform3D(mesh, transformation, id, flags);
}, "Transform 3D positions, normals, tangents and bitangents in a mesh data", py::arg("mesh"), py::arg("transformation"), py::arg("id") = 0, py::arg("flags") = MeshTools::InterleaveFlag::PreserveInterleavedAttributes)
.def("transform3d_in_place", [](Trade::MeshData& mesh, const Matrix4& transformation, UnsignedInt id) {
return MeshTools::transform3D(mesh, transformation, id, morphTargetId, flags);
}, "Transform 3D positions, normals, tangents and bitangents in a mesh data", py::arg("mesh"), py::arg("transformation"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
py::kw_only{}, /* new in pybind11 2.6 */
#endif
py::arg("id") = 0, py::arg("morph_target_id") = -1, py::arg("flags") = MeshTools::InterleaveFlag::PreserveInterleavedAttributes)
.def("transform3d_in_place", [](Trade::MeshData& mesh, const Matrix4& transformation, UnsignedInt id, Int morphTargetId) {
if(!(mesh.vertexDataFlags() & Trade::DataFlag::Mutable)) {
PyErr_SetString(PyExc_AssertionError, "vertex data not mutable");
throw py::error_already_set{};
}
const Containers::Optional<UnsignedInt> positionAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Position, id);
const Containers::Optional<UnsignedInt> positionAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Position, id, morphTargetId);
if(!positionAttributeId) {
PyErr_SetString(PyExc_KeyError, "position attribute not found");
throw py::error_already_set{};
@ -251,9 +263,9 @@ void meshtools(py::module_& m) {
throw py::error_already_set{};
}
const Containers::Optional<UnsignedInt> tangentAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Tangent, id);
const Containers::Optional<UnsignedInt> bitangentAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Bitangent, id);
const Containers::Optional<UnsignedInt> normalAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Normal, id);
const Containers::Optional<UnsignedInt> tangentAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Tangent, id, morphTargetId);
const Containers::Optional<UnsignedInt> bitangentAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Bitangent, id, morphTargetId);
const Containers::Optional<UnsignedInt> normalAttributeId = mesh.findAttributeId(Trade::MeshAttribute::Normal, id, morphTargetId);
if(tangentAttributeId &&
(mesh.attributeFormat(*tangentAttributeId) != VertexFormat::Vector3 &&
mesh.attributeFormat(*tangentAttributeId) != VertexFormat::Vector4))
@ -270,10 +282,14 @@ void meshtools(py::module_& m) {
throw py::error_already_set{};
}
MeshTools::transform3DInPlace(mesh, transformation, id);
}, "Transform 3D position, normals, tangents and bitangents in a mesh data in-place", py::arg("mesh"), py::arg("transformation"), py::arg("id") = 0)
.def("transform_texture_coordinates2d", [](const Trade::MeshData& mesh, const Matrix3& transformation, UnsignedInt id, MeshTools::InterleaveFlag flags) {
const Containers::Optional<UnsignedInt> textureCoordinateAttributeId = mesh.findAttributeId(Trade::MeshAttribute::TextureCoordinates, id);
MeshTools::transform3DInPlace(mesh, transformation, id, morphTargetId);
}, "Transform 3D position, normals, tangents and bitangents in a mesh data in-place", py::arg("mesh"), py::arg("transformation"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
py::kw_only{}, /* new in pybind11 2.6 */
#endif
py::arg("id") = 0, py::arg("morph_target_id") = -1)
.def("transform_texture_coordinates2d", [](const Trade::MeshData& mesh, const Matrix3& transformation, UnsignedInt id, Int morphTargetId, MeshTools::InterleaveFlag flags) {
const Containers::Optional<UnsignedInt> textureCoordinateAttributeId = mesh.findAttributeId(Trade::MeshAttribute::TextureCoordinates, id, morphTargetId);
if(!textureCoordinateAttributeId) {
PyErr_SetString(PyExc_KeyError, "texture coordinates attribute not found");
throw py::error_already_set{};
@ -281,15 +297,19 @@ void meshtools(py::module_& m) {
/** @todo check that the texture coordinates aren't impl-specific
once it's possible to test */
return MeshTools::transformTextureCoordinates2D(mesh, transformation, id, flags);
}, "Transform 2D texture coordinates in a mesh data", py::arg("mesh"), py::arg("transformation"), py::arg("id") = 0, py::arg("flags") = MeshTools::InterleaveFlag::PreserveInterleavedAttributes)
.def("transform_texture_coordinates2d_in_place", [](Trade::MeshData& mesh, const Matrix3& transformation, UnsignedInt id) {
return MeshTools::transformTextureCoordinates2D(mesh, transformation, id, morphTargetId, flags);
}, "Transform 2D texture coordinates in a mesh data", py::arg("mesh"), py::arg("transformation"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
py::kw_only{}, /* new in pybind11 2.6 */
#endif
py::arg("id") = 0, py::arg("morph_target_id") = -1, py::arg("flags") = MeshTools::InterleaveFlag::PreserveInterleavedAttributes)
.def("transform_texture_coordinates2d_in_place", [](Trade::MeshData& mesh, const Matrix3& transformation, UnsignedInt id, Int morphTargetId) {
if(!(mesh.vertexDataFlags() & Trade::DataFlag::Mutable)) {
PyErr_SetString(PyExc_AssertionError, "vertex data not mutable");
throw py::error_already_set{};
}
const Containers::Optional<UnsignedInt> textureCoordinateAttributeId = mesh.findAttributeId(Trade::MeshAttribute::TextureCoordinates, id);
const Containers::Optional<UnsignedInt> textureCoordinateAttributeId = mesh.findAttributeId(Trade::MeshAttribute::TextureCoordinates, id, morphTargetId);
if(!textureCoordinateAttributeId) {
PyErr_SetString(PyExc_KeyError, "texture coordinates attribute not found");
throw py::error_already_set{};
@ -299,8 +319,12 @@ void meshtools(py::module_& m) {
throw py::error_already_set{};
}
MeshTools::transformTextureCoordinates2DInPlace(mesh, transformation, id);
}, "Transform 2D texture coordinates in a mesh data in-place", py::arg("mesh"), py::arg("transformation"), py::arg("id") = 0);
MeshTools::transformTextureCoordinates2DInPlace(mesh, transformation, id, morphTargetId);
}, "Transform 2D texture coordinates in a mesh data in-place", py::arg("mesh"), py::arg("transformation"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
py::kw_only{}, /* new in pybind11 2.6 */
#endif
py::arg("id") = 0, py::arg("morph_target_id") = -1);
}
}

32
src/python/magnum/test/test_meshtools.py

@ -248,6 +248,9 @@ class RemoveDuplicates(unittest.TestCase):
self.assertEqual(single_point.vertex_count, 1)
class Transform(unittest.TestCase):
# TODO test everything with explicit morph target once there's support in
# some importer
def test_2d(self):
mesh = primitives.line2d()
self.assertEqual(mesh.attribute(trade.MeshAttribute.POSITION)[0], (0.0, 0.0))
@ -293,18 +296,33 @@ class Transform(unittest.TestCase):
def test_no_attribute(self):
mesh = meshtools.copy(primitives.square_solid(primitives.SquareFlags.TEXTURE_COORDINATES))
# ID not found
with self.assertRaisesRegex(KeyError, "position attribute not found"):
meshtools.transform2d(mesh, Matrix3(), id=1)
with self.assertRaisesRegex(KeyError, "position attribute not found"):
meshtools.transform2d_in_place(mesh, Matrix3(), id=1)
with self.assertRaisesRegex(KeyError, "position attribute not found"):
meshtools.transform3d(mesh, Matrix4(), id=1)
with self.assertRaisesRegex(KeyError, "position attribute not found"):
meshtools.transform3d_in_place(mesh, Matrix4(), id=1)
with self.assertRaisesRegex(KeyError, "texture coordinates attribute not found"):
meshtools.transform_texture_coordinates2d(mesh, Matrix3(), id=1)
with self.assertRaisesRegex(KeyError, "texture coordinates attribute not found"):
meshtools.transform_texture_coordinates2d_in_place(mesh, Matrix3(), id=1)
# Morph target not found
with self.assertRaisesRegex(KeyError, "position attribute not found"):
meshtools.transform2d(mesh, Matrix3(), 1)
meshtools.transform2d(mesh, Matrix3(), morph_target_id=37)
with self.assertRaisesRegex(KeyError, "position attribute not found"):
meshtools.transform2d_in_place(mesh, Matrix3(), 1)
meshtools.transform2d_in_place(mesh, Matrix3(), morph_target_id=37)
with self.assertRaisesRegex(KeyError, "position attribute not found"):
meshtools.transform3d(mesh, Matrix4(), 1)
meshtools.transform3d(mesh, Matrix4(), morph_target_id=37)
with self.assertRaisesRegex(KeyError, "position attribute not found"):
meshtools.transform3d_in_place(mesh, Matrix4(), 1)
meshtools.transform3d_in_place(mesh, Matrix4(), morph_target_id=37)
with self.assertRaisesRegex(KeyError, "texture coordinates attribute not found"):
meshtools.transform_texture_coordinates2d(mesh, Matrix3(), 1)
meshtools.transform_texture_coordinates2d(mesh, Matrix3(), morph_target_id=37)
with self.assertRaisesRegex(KeyError, "texture coordinates attribute not found"):
meshtools.transform_texture_coordinates2d_in_place(mesh, Matrix3(), 1)
meshtools.transform_texture_coordinates2d_in_place(mesh, Matrix3(), morph_target_id=37)
def test_not_2d_not_3d(self):
mesh2d = primitives.line2d()
@ -341,6 +359,8 @@ class Transform(unittest.TestCase):
meshtools.transform2d_in_place(importer.mesh('packed positions'), Matrix3())
with self.assertRaisesRegex(AssertionError, "positions are not VECTOR3"):
meshtools.transform3d_in_place(importer.mesh('packed positions'), Matrix4())
# TODO test also with an explicit ID and morph target ID to verify it's
# correctly propagated
with self.assertRaisesRegex(AssertionError, "normals are not VECTOR3"):
meshtools.transform3d_in_place(importer.mesh('packed normals'), Matrix4())
with self.assertRaisesRegex(AssertionError, "tangents are not VECTOR3 or VECTOR4"):

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

@ -267,6 +267,7 @@ class MeshData(unittest.TestCase):
self.assertEqual(mesh.vertex_count, 3)
self.assertEqual(mesh.attribute_count(), 9)
self.assertEqual(mesh.attribute_count(morph_target_id=37), 0)
# Attribute properties by ID
self.assertEqual(mesh.attribute_name(2), trade.MeshAttribute.POSITION)
@ -292,11 +293,12 @@ class MeshData(unittest.TestCase):
self.assertTrue(mesh.has_attribute(trade.MeshAttribute.COLOR))
self.assertTrue(mesh.has_attribute(trade.MeshAttribute.POSITION))
self.assertFalse(mesh.has_attribute(trade.MeshAttribute.TANGENT))
self.assertFalse(mesh.has_attribute(trade.MeshAttribute.POSITION, morph_target_id=37))
self.assertEqual(mesh.attribute_count(trade.MeshAttribute.POSITION), 1)
self.assertEqual(mesh.attribute_count(trade.MeshAttribute.TEXTURE_COORDINATES), 2)
self.assertEqual(mesh.attribute_count(trade.MeshAttribute.TANGENT), 0)
self.assertEqual(mesh.attribute_id(trade.MeshAttribute.POSITION), 2)
self.assertEqual(mesh.attribute_id(trade.MeshAttribute.TEXTURE_COORDINATES, 1), 4)
self.assertEqual(mesh.attribute_id(trade.MeshAttribute.TEXTURE_COORDINATES, id=1), 4)
self.assertEqual(mesh.attribute_format(trade.MeshAttribute.COLOR), VertexFormat.VECTOR3UB_NORMALIZED)
self.assertEqual(mesh.attribute_format(trade.MeshAttribute.OBJECT_ID), VertexFormat.UNSIGNED_INT)
self.assertEqual(mesh.attribute_offset(trade.MeshAttribute.COLOR), 20)
@ -631,19 +633,35 @@ class MeshData(unittest.TestCase):
# Access by existing name + OOB ID
with self.assertRaises(KeyError):
mesh.attribute_id(trade.MeshAttribute.TEXTURE_COORDINATES, 2)
mesh.attribute_id(trade.MeshAttribute.TEXTURE_COORDINATES, id=2)
with self.assertRaises(KeyError):
mesh.attribute_format(trade.MeshAttribute.TEXTURE_COORDINATES, 2)
mesh.attribute_format(trade.MeshAttribute.TEXTURE_COORDINATES, id=2)
with self.assertRaises(KeyError):
mesh.attribute_offset(trade.MeshAttribute.TEXTURE_COORDINATES, 2)
mesh.attribute_offset(trade.MeshAttribute.TEXTURE_COORDINATES, id=2)
with self.assertRaises(KeyError):
mesh.attribute_stride(trade.MeshAttribute.TEXTURE_COORDINATES, 2)
mesh.attribute_stride(trade.MeshAttribute.TEXTURE_COORDINATES, id=2)
with self.assertRaises(KeyError):
mesh.attribute_array_size(trade.MeshAttribute.TEXTURE_COORDINATES, 2)
mesh.attribute_array_size(trade.MeshAttribute.TEXTURE_COORDINATES, id=2)
with self.assertRaises(KeyError):
mesh.attribute(trade.MeshAttribute.TEXTURE_COORDINATES, 2)
mesh.attribute(trade.MeshAttribute.TEXTURE_COORDINATES, id=2)
with self.assertRaises(KeyError):
mesh.mutable_attribute(trade.MeshAttribute.TEXTURE_COORDINATES, 2)
mesh.mutable_attribute(trade.MeshAttribute.TEXTURE_COORDINATES, id=2)
# Access by existing name + OOB morph target ID
with self.assertRaises(KeyError):
mesh.attribute_id(trade.MeshAttribute.TEXTURE_COORDINATES, morph_target_id=37)
with self.assertRaises(KeyError):
mesh.attribute_format(trade.MeshAttribute.TEXTURE_COORDINATES, morph_target_id=37)
with self.assertRaises(KeyError):
mesh.attribute_offset(trade.MeshAttribute.TEXTURE_COORDINATES, morph_target_id=37)
with self.assertRaises(KeyError):
mesh.attribute_stride(trade.MeshAttribute.TEXTURE_COORDINATES, morph_target_id=37)
with self.assertRaises(KeyError):
mesh.attribute_array_size(trade.MeshAttribute.TEXTURE_COORDINATES, morph_target_id=37)
with self.assertRaises(KeyError):
mesh.attribute(trade.MeshAttribute.TEXTURE_COORDINATES, morph_target_id=37)
with self.assertRaises(KeyError):
mesh.mutable_attribute(trade.MeshAttribute.TEXTURE_COORDINATES, morph_target_id=37)
def test_attribute_access_array(self):
importer = trade.ImporterManager().load_and_instantiate('GltfImporter')

89
src/python/magnum/trade.cpp

@ -955,12 +955,25 @@ void trade(py::module_& m) {
}, "Mutable indices")
.def_property_readonly("vertex_count", &Trade::MeshData::vertexCount, "Vertex count")
/* Has to be a function instead of a property because there's an
overload taking a name */
overload taking a morph target ID and an overload taking a name */
.def("attribute_count", static_cast<UnsignedInt(Trade::MeshData::*)() const>(&Trade::MeshData::attributeCount), "Attribute array count")
.def("attribute_count", static_cast<UnsignedInt(Trade::MeshData::*)(Int) const>(&Trade::MeshData::attributeCount), "Attribute array count for given morph target",
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
py::kw_only{}, /* new in pybind11 2.6 */
#endif
py::arg("morph_target_id"))
/** @todo direct access to MeshAttributeData, once making custom
MeshData is desired */
.def("has_attribute", &Trade::MeshData::hasAttribute, "Whether the mesh has given attribute", py::arg("name"))
.def("attribute_count", static_cast<UnsignedInt(Trade::MeshData::*)(Trade::MeshAttribute) const>(&Trade::MeshData::attributeCount), "Count of given named attribute", py::arg("name"))
.def("has_attribute", &Trade::MeshData::hasAttribute, "Whether the mesh has given attribute", py::arg("name"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
py::kw_only{}, /* new in pybind11 2.6 */
#endif
py::arg("morph_target_id") = -1)
.def("attribute_count", static_cast<UnsignedInt(Trade::MeshData::*)(Trade::MeshAttribute, Int) const>(&Trade::MeshData::attributeCount), "Count of given named attribute", py::arg("name"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
py::kw_only{}, /* new in pybind11 2.6 */
#endif
py::arg("morph_target_id") = -1)
/* IMPORTANT: due to pybind11 behavioral differences on (already EOL'd)
Python 3.7 the following overloads need to have the MeshAttribute
@ -974,12 +987,16 @@ void trade(py::module_& m) {
}
return self.attributeName(id);
}, "Attribute name", py::arg("id"))
.def("attribute_id", [](Trade::MeshData& self, Trade::MeshAttribute name, UnsignedInt id) {
if(const Containers::Optional<UnsignedInt> found = self.findAttributeId(name, id))
.def("attribute_id", [](Trade::MeshData& self, Trade::MeshAttribute name, UnsignedInt id, Int morphTargetId) {
if(const Containers::Optional<UnsignedInt> found = self.findAttributeId(name, id, morphTargetId))
return *found;
PyErr_SetNone(PyExc_KeyError);
throw py::error_already_set{};
}, "Absolute ID of a named attribute", py::arg("name"), py::arg("id") = 0)
}, "Absolute ID of a named attribute", py::arg("name"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
py::kw_only{}, /* new in pybind11 2.6 */
#endif
py::arg("id") = 0, py::arg("morph_target_id") = -1)
.def("attribute_id", [](Trade::MeshData& self, UnsignedInt id) {
if(id >= self.attributeCount()) {
PyErr_SetNone(PyExc_IndexError);
@ -987,12 +1004,16 @@ void trade(py::module_& m) {
}
return self.attributeId(id);
}, "Attribute ID in a set of attributes of the same name", py::arg("id"))
.def("attribute_format", [](Trade::MeshData& self, Trade::MeshAttribute name, UnsignedInt id) {
if(const Containers::Optional<UnsignedInt> found = self.findAttributeId(name, id))
.def("attribute_format", [](Trade::MeshData& self, Trade::MeshAttribute name, UnsignedInt id, Int morphTargetId) {
if(const Containers::Optional<UnsignedInt> found = self.findAttributeId(name, id, morphTargetId))
return self.attributeFormat(*found);
PyErr_SetNone(PyExc_KeyError);
throw py::error_already_set{};
}, "Format of a named attribute", py::arg("name"), py::arg("id") = 0)
}, "Format of a named attribute", py::arg("name"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
py::kw_only{}, /* new in pybind11 2.6 */
#endif
py::arg("id") = 0, py::arg("morph_target_id") = -1)
.def("attribute_format", [](Trade::MeshData& self, UnsignedInt id) {
if(id >= self.attributeCount()) {
PyErr_SetNone(PyExc_IndexError);
@ -1000,12 +1021,16 @@ void trade(py::module_& m) {
}
return self.attributeFormat(id);
}, "Attribute format", py::arg("id"))
.def("attribute_offset", [](Trade::MeshData& self, Trade::MeshAttribute name, UnsignedInt id) {
if(const Containers::Optional<UnsignedInt> found = self.findAttributeId(name, id))
.def("attribute_offset", [](Trade::MeshData& self, Trade::MeshAttribute name, UnsignedInt id, Int morphTargetId) {
if(const Containers::Optional<UnsignedInt> found = self.findAttributeId(name, id, morphTargetId))
return self.attributeOffset(*found);
PyErr_SetNone(PyExc_KeyError);
throw py::error_already_set{};
}, "Offset of a named attribute", py::arg("name"), py::arg("id") = 0)
}, "Offset of a named attribute", py::arg("name"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
py::kw_only{}, /* new in pybind11 2.6 */
#endif
py::arg("id") = 0, py::arg("morph_target_id") = -1)
.def("attribute_offset", [](Trade::MeshData& self, UnsignedInt id) {
if(id >= self.attributeCount()) {
PyErr_SetNone(PyExc_IndexError);
@ -1013,12 +1038,16 @@ void trade(py::module_& m) {
}
return self.attributeOffset(id);
}, "Attribute offset", py::arg("id"))
.def("attribute_stride", [](Trade::MeshData& self, Trade::MeshAttribute name, UnsignedInt id) {
if(const Containers::Optional<UnsignedInt> found = self.findAttributeId(name, id))
.def("attribute_stride", [](Trade::MeshData& self, Trade::MeshAttribute name, UnsignedInt id, Int morphTargetId) {
if(const Containers::Optional<UnsignedInt> found = self.findAttributeId(name, id, morphTargetId))
return self.attributeStride(*found);
PyErr_SetNone(PyExc_KeyError);
throw py::error_already_set{};
}, "Stride of a named attribute", py::arg("name"), py::arg("id") = 0)
}, "Stride of a named attribute", py::arg("name"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
py::kw_only{}, /* new in pybind11 2.6 */
#endif
py::arg("id") = 0, py::arg("morph_target_id") = -1)
.def("attribute_stride", [](Trade::MeshData& self, UnsignedInt id) {
if(id >= self.attributeCount()) {
PyErr_SetNone(PyExc_IndexError);
@ -1026,12 +1055,16 @@ void trade(py::module_& m) {
}
return self.attributeStride(id);
}, "Attribute stride", py::arg("id"))
.def("attribute_array_size", [](Trade::MeshData& self, Trade::MeshAttribute name, UnsignedInt id) {
if(const Containers::Optional<UnsignedInt> found = self.findAttributeId(name, id))
.def("attribute_array_size", [](Trade::MeshData& self, Trade::MeshAttribute name, UnsignedInt id, Int morphTargetId) {
if(const Containers::Optional<UnsignedInt> found = self.findAttributeId(name, id, morphTargetId))
return self.attributeArraySize(*found);
PyErr_SetNone(PyExc_KeyError);
throw py::error_already_set{};
}, "Array size of a named attribute", py::arg("name"), py::arg("id") = 0)
}, "Array size of a named attribute", py::arg("name"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
py::kw_only{}, /* new in pybind11 2.6 */
#endif
py::arg("id") = 0, py::arg("morph_target_id") = -1)
.def("attribute_array_size", [](Trade::MeshData& self, UnsignedInt id) {
if(id >= self.attributeCount()) {
PyErr_SetNone(PyExc_IndexError);
@ -1039,8 +1072,8 @@ void trade(py::module_& m) {
}
return self.attributeArraySize(id);
}, "Attribute array size", py::arg("id"))
.def("attribute", [](Trade::MeshData& self, Trade::MeshAttribute name, UnsignedInt id) {
const Containers::Optional<UnsignedInt> found = self.findAttributeId(name, id);
.def("attribute", [](Trade::MeshData& self, Trade::MeshAttribute name, UnsignedInt id, Int morphTargetId) {
const Containers::Optional<UnsignedInt> found = self.findAttributeId(name, id, morphTargetId);
if(!found) {
PyErr_SetNone(PyExc_KeyError);
throw py::error_already_set{};
@ -1052,7 +1085,11 @@ void trade(py::module_& m) {
throw py::error_already_set{};
}
return meshAttributeView(self, *found, self.attribute(*found));
}, "Data for given named attribute", py::arg("name"), py::arg("id") = 0)
}, "Data for given named attribute", py::arg("name"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
py::kw_only{}, /* new in pybind11 2.6 */
#endif
py::arg("id") = 0, py::arg("morph_target_id") = -1)
.def("attribute", [](Trade::MeshData& self, UnsignedInt id) {
if(id >= self.attributeCount()) {
PyErr_SetNone(PyExc_IndexError);
@ -1066,8 +1103,8 @@ void trade(py::module_& m) {
}
return meshAttributeView(self, id, self.attribute(id));
}, "Data for given attribute", py::arg("id"))
.def("mutable_attribute", [](Trade::MeshData& self, Trade::MeshAttribute name, UnsignedInt id) {
const Containers::Optional<UnsignedInt> found = self.findAttributeId(name, id);
.def("mutable_attribute", [](Trade::MeshData& self, Trade::MeshAttribute name, UnsignedInt id, Int morphTargetId) {
const Containers::Optional<UnsignedInt> found = self.findAttributeId(name, id, morphTargetId);
if(!found) {
PyErr_SetNone(PyExc_KeyError);
throw py::error_already_set{};
@ -1083,7 +1120,11 @@ void trade(py::module_& m) {
throw py::error_already_set{};
}
return meshAttributeView(self, *found, self.mutableAttribute(*found));
}, "Mutable data for given named attribute", py::arg("name"), py::arg("id") = 0)
}, "Mutable data for given named attribute", py::arg("name"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
py::kw_only{}, /* new in pybind11 2.6 */
#endif
py::arg("id") = 0, py::arg("morph_target_id") = -1)
.def("mutable_attribute", [](Trade::MeshData& self, UnsignedInt id) {
if(id >= self.attributeCount()) {
PyErr_SetNone(PyExc_IndexError);

Loading…
Cancel
Save