diff --git a/doc/python/magnum.trade.rst b/doc/python/magnum.trade.rst index 5be9476..3f593ec 100644 --- a/doc/python/magnum.trade.rst +++ b/doc/python/magnum.trade.rst @@ -119,6 +119,13 @@ :ref:`mutable_indices` and :ref:`mutable_attribute()`, for example to perform a static transformation of the mesh before passing it to OpenGL. + Normalized formats (such as :ref:`VertexFormat.VECTOR3UB_NORMALIZED`) are + unpacked to a corresponding floating-point representation in element access + and packed from a floating-point representation in mutable acess. The type + annotation is however still matching the original type (such as :py:`'3B'` + in this case), so code consuming these via the buffer protocol needs to + handle the normalization explicitly if needed. + .. py:property:: magnum.trade.MeshData.mutable_index_data :raise AttributeError: If :ref:`index_data_flags` doesn't contain :ref:`DataFlag.MUTABLE` diff --git a/src/python/magnum/test/test_trade.py b/src/python/magnum/test/test_trade.py index 21450eb..e8f3331 100644 --- a/src/python/magnum/test/test_trade.py +++ b/src/python/magnum/test/test_trade.py @@ -461,6 +461,23 @@ class MeshData(unittest.TestCase): mutable_packed[1] -= Vector3i(12, 56, 200) self.assertEqual(packed[1], Vector3(39, 46, 55)) + def test_normalized_attribute_access(self): + importer = trade.ImporterManager().load_and_instantiate('GltfImporter') + importer.open_file(os.path.join(os.path.dirname(__file__), 'mesh.gltf')) + + mesh = importer.mesh(0) + self.assertEqual(mesh.index_data_flags, trade.DataFlag.OWNED|trade.DataFlag.MUTABLE) + self.assertEqual(mesh.attribute_format(trade.MeshAttribute.COLOR), VertexFormat.VECTOR3UB_NORMALIZED) + + normalized = mesh.attribute(trade.MeshAttribute.COLOR) + mutable_normalized = mesh.mutable_attribute(trade.MeshAttribute.COLOR) + self.assertEqual(normalized[1], Vector3(0.2, 0.4, 1)) + self.assertEqual(mutable_normalized[1], Vector3(0.2, 0.4, 1)) + + mutable_normalized[1] *= 0.5 + # Rounding errors are expected + self.assertEqual(normalized[1], Vector3(0.101961, 0.2, 0.501961)) + def test_data_access_not_mutable(self): mesh = primitives.cube_solid() # TODO split this once there's a mesh where only one or the other would diff --git a/src/python/magnum/trade.cpp b/src/python/magnum/trade.cpp index 6bbffad..7d2be8d 100644 --- a/src/python/magnum/trade.cpp +++ b/src/python/magnum/trade.cpp @@ -29,8 +29,8 @@ #include /** @todo drop once we have our string casters */ #include #include -#include #include +#include #include #include #include @@ -489,47 +489,75 @@ Containers::Triple(item) = format(py::cast(object)); \ }}; + /* Normalized types that need to be packed/unpacked before passed + from/to pybind */ + #define _cNormalized(format, unpackType) \ + case VertexFormat::format ## Normalized: return { \ + Containers::Implementation::pythonFormatString(), \ + [](const char* item) { \ + return py::cast(Math::unpack(*reinterpret_cast(item))); \ + }, \ + [](char* item, py::handle object) { \ + *reinterpret_cast(item) = Math::pack(py::cast(object)); \ + }}; /* LCOV_EXCL_START */ _c(Float) _c(Double) _c(UnsignedByte) + _cNormalized(UnsignedByte, Float) _c(Byte) + _cNormalized(Byte, Float) _c(UnsignedShort) + _cNormalized(UnsignedShort, Float) _c(Short) + _cNormalized(Short, Float) _c(UnsignedInt) _c(Int) _c(Vector2) _c(Vector2d) _cc(Vector2ub, Vector2ui) + _cNormalized(Vector2ub, Vector2) _cc(Vector2b, Vector2i) + _cNormalized(Vector2b, Vector2) _cc(Vector2us, Vector2ui) + _cNormalized(Vector2us, Vector2) _cc(Vector2s, Vector2i) + _cNormalized(Vector2s, Vector2) _c(Vector2ui) _c(Vector2i) _c(Vector3) _c(Vector3d) _cc(Vector3ub, Vector3ui) + _cNormalized(Vector3ub, Vector3) _cc(Vector3b, Vector3i) + _cNormalized(Vector3b, Vector3) _cc(Vector3us, Vector3ui) + _cNormalized(Vector3us, Vector3) _cc(Vector3s, Vector3i) + _cNormalized(Vector3s, Vector3) _c(Vector3ui) _c(Vector3i) _c(Vector4) _c(Vector4d) _cc(Vector4ub, Vector4ui) + _cNormalized(Vector4ub, Vector4) _cc(Vector4b, Vector4i) + _cNormalized(Vector4b, Vector4) _cc(Vector4us, Vector4ui) + _cNormalized(Vector4us, Vector4) _cc(Vector4s, Vector4i) + _cNormalized(Vector4s, Vector4) _c(Vector4ui) _c(Vector4i) /* LCOV_EXCL_STOP */ #undef _c #undef _cc + #undef _cNormalized - /** @todo handle half, normalized and matrix types */ + /** @todo handle half and matrix types */ default: return {}; }