Browse Source

python: expose trade.MeshAttributeData.

To be eventually used for creating MeshData from scratch.
next
Vladimír Vondruš 2 years ago
parent
commit
c968b91a41
  1. 111
      doc/python/magnum.trade.rst
  2. 9
      doc/python/pages/changelog.rst
  3. 5
      src/Magnum/Trade/PythonBindings.h
  4. 175
      src/python/magnum/test/test_trade.py
  5. 93
      src/python/magnum/trade.cpp

111
doc/python/magnum.trade.rst

@ -135,6 +135,117 @@
>>> attribute.custom_value
17
.. py:class:: magnum.trade.MeshAttributeData
Associates a typed data view with a name, vertex format and other mesh
attribute properties. The data view can be either one-dimensional, for
example a NumPy array:
..
Just to verify the snippet below actually works (don't want the arrows
shown in the docs, want to have it nicely wrapped)
..
>>> from magnum import *
>>> import numpy as np
>>> data = np.array([(-0.5, 0.0), (+0.5, 0.0), ( 0.0, 0.5)], dtype='2f')
>>> positions = trade.MeshAttributeData(trade.MeshAttribute.POSITION, VertexFormat.VECTOR2, data)
.. code:: py
data = np.array([(-0.5, 0.0),
(+0.5, 0.0),
( 0.0, 0.5)], dtype='2f')
positions = trade.MeshAttributeData(
trade.MeshAttribute.POSITION,
VertexFormat.VECTOR2,
data)
Or it can be two-dimensional, for example by expanding a flat array into a
list of two-component vectors:
..
Again to verify the snippet below actually works
..
>>> from corrade import containers
>>> import array
>>> data = array.array('f', [-0.5, 0.0, +0.5, 0.0, 0.0, 0.5])
>>> positions = trade.MeshAttributeData(trade.MeshAttribute.POSITION, VertexFormat.VECTOR2, containers.StridedArrayView1D(data).expanded(0, (3, 2)))
.. code:: py
data = array.array('f', [-0.5, 0.0,
+0.5, 0.0,
0.0, 0.5])
positions = trade.MeshAttributeData(
trade.MeshAttribute.POSITION,
VertexFormat.VECTOR2,
containers.StridedArrayView1D(data).expanded(0, (3, 2)))
`Memory ownership and reference counting`_
==========================================
On initialization, the instance inherits the
:ref:`containers.StridedArrayView1D.owner <corrade.containers.StridedArrayView1D.owner>`
object, storing it in the :ref:`owner` field, meaning that calling
:py:`del` on the original data will *not* invalidate the instance.
`Data access`_
==============
Similarly to :ref:`MeshData`, the class makes use of Python's dynamic
nature and provides direct access to attribute data in their concrete type
via :ref:`data`. However, the :ref:`MeshAttributeData` is considered a low
level API and thus a :ref:`containers.StridedArrayView2D <corrade.containers.StridedArrayView2D>`
is returned always, even for non-array attributes. The returned view
inherits the :ref:`owner` and element access coverts to a type
corresponding to a particular :ref:`VertexFormat`. For example, extracting
the data from the :py:`positions` attribute created above:
.. code:: pycon
>>> view = positions.data
>>> view.owner is data
True
>>> view[1][0]
Vector(0.5, 0)
.. py:function:: magnum.trade.MeshAttributeData.__init__(self, name: magnum.trade.MeshAttribute, format: magnum.VertexFormat, data: corrade.containers.StridedArrayView1D, array_size: int, morph_target_id: int)
:raise AssertionError: If :p:`format` is not valid for :p:`name`
:raise AssertionError: If :p:`data` size doesn't fit into 32 bits
:raise AssertionError: If :p:`data` stride doesn't fit into 16 bits
:raise AssertionError: If :p:`data` format size is smaller than size of
:p:`format` at given :p:`array_size`
:raise AssertionError: If :p:`morph_target_id` is less than :py:`-1` or
greater than :py:`127`
:raise AssertionError: If :p:`morph_target_id` is not allowed for :p:`name`
:raise AssertionError: If :p:`array_size` is zero and :p:`name` is an array
attribute
:raise AssertionError: If :p:`array_size` is non-zero and :p:`name` can't
be an array attribute
.. py:function:: magnum.trade.MeshAttributeData.__init__(self, name: magnum.trade.MeshAttribute, format: magnum.VertexFormat, data: corrade.containers.StridedArrayView2D, array_size: int, morph_target_id: int)
:raise AssertionError: If :p:`format` is not valid for :p:`name`
:raise AssertionError: If :p:`data` first dimension size doesn't fit into
32 bits
:raise AssertionError: If :p:`data` first dimension stride doesn't fit into
16 bits
:raise AssertionError: If :p:`data` second dimension isn't contiguous
:raise AssertionError: If :p:`data` format size times second dimension size
is smaller than size of :p:`format` at given :p:`array_size`
:raise AssertionError: If :p:`morph_target_id` is less than :py:`-1` or
greater than :py:`127`
:raise AssertionError: If :p:`morph_target_id` is not allowed for :p:`name`
:raise AssertionError: If :p:`array_size` is zero and :p:`name` is an array
attribute
:raise AssertionError: If :p:`array_size` is non-zero and :p:`name` can't
be an array attribute
.. py:property:: magnum.trade.MeshAttributeData.data
:raise NotImplementedError: If :ref:`format <MeshAttributeData.format>` is
a half-float or matrix type
A 2D view is returned always, non-array attributes have the second
dimension size :py:`1`.
.. py:class:: magnum.trade.MeshData
:TODO: remove this line once m.css stops ignoring first caption on a page

9
doc/python/pages/changelog.rst

@ -146,10 +146,11 @@ Changelog
:ref:`trade.AbstractImporter.flags` and corresponding enums
- Exposed a basic interface of :ref:`trade.AbstractImageConverter` and
:ref:`trade.AbstractSceneConverter`
- Exposed the whole interface of :ref:`trade.MeshData` including typed access
to index and attribute data, together with :ref:`VertexFormat`,
:ref:`trade.DataFlags`, :ref:`trade.AbstractImporter.mesh_attribute_name()`
and :ref:`trade.AbstractImporter.mesh_attribute_for_name()`
- Exposed the whole interface of :ref:`trade.MeshData` and
:ref:`trade.MeshAttributeData` including typed access to index and
attribute data, together with :ref:`VertexFormat`, :ref:`trade.DataFlags`,
:ref:`trade.AbstractImporter.mesh_attribute_name()` and
:ref:`trade.AbstractImporter.mesh_attribute_for_name()`
- Exposed the whole interface of :ref:`trade.MaterialData` including typed
access to attribute data, together with
:ref:`trade.AbstractImporter.material()` and related importer APIs

5
src/Magnum/Trade/PythonBindings.h

@ -50,6 +50,9 @@ inline bool pyDataFlagsNeedOwner(const MeshData& data) {
!(data.indexDataFlags() & (DataFlag::Owned|DataFlag::Global)) ||
!(data.vertexDataFlags() & (DataFlag::Owned|DataFlag::Global));
}
inline bool pyDataFlagsNeedOwner(const MeshAttributeData& data) {
return data.data().data();
}
}
@ -59,7 +62,7 @@ inline bool pyDataFlagsNeedOwner(const MeshData& data) {
unnecessarily complex */
template<class T> struct PyDataHolder: std::unique_ptr<T> {
explicit PyDataHolder(T* object): PyDataHolder{object, pybind11::none{}} {
/* Data without an owner can only be self-owned or global */
/* Data without an owner can only be self-owned, global or empty */
CORRADE_INTERNAL_ASSERT(!Implementation::pyDataFlagsNeedOwner(*object));
}

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

@ -23,6 +23,7 @@
# DEALINGS IN THE SOFTWARE.
#
import array
import os
import platform
import sys
@ -603,6 +604,180 @@ class MaterialData(unittest.TestCase):
with self.assertRaisesRegex(KeyError, "MaterialAttribute.DIFFUSE_TEXTURE not found in the base material"):
material.attribute(trade.MaterialAttribute.DIFFUSE_TEXTURE)
class MeshAttributeData(unittest.TestCase):
def test_init_1d(self):
a = array.array('H', [3, 7, 16, 29998])
a_refcount = sys.getrefcount(a)
b = trade.MeshAttributeData(trade.MeshAttribute.OBJECT_ID, VertexFormat.UNSIGNED_SHORT, a)
b_refcount = sys.getrefcount(b)
self.assertEqual(b.name, trade.MeshAttribute.OBJECT_ID)
self.assertEqual(b.format, VertexFormat.UNSIGNED_SHORT)
self.assertEqual(b.array_size, 0)
self.assertEqual(b.morph_target_id, -1)
self.assertIs(b.owner, a)
self.assertEqual(sys.getrefcount(a), a_refcount + 1)
data = b.data
self.assertEqual(data.size, (4, 1))
self.assertEqual(data.stride, (2, 2))
self.assertEqual(data.format, 'H')
self.assertIs(data.owner, a)
# Returns a 2D view always, transpose and take the first element to
# "flatten" it.
self.assertEqual(list(data.transposed(0, 1)[0]), [3, 7, 16, 29998])
# The data reference the original array, not the MeshAttributeData
# instance
self.assertEqual(sys.getrefcount(b), b_refcount)
self.assertEqual(sys.getrefcount(a), a_refcount + 2)
del b
self.assertEqual(sys.getrefcount(a), a_refcount + 1)
del data
self.assertEqual(sys.getrefcount(a), a_refcount)
def test_init_2d(self):
a = array.array('f', [1.0, 0.0, 0.0,
0.0, -1.0, 0.0])
a_refcount = sys.getrefcount(a)
b = trade.MeshAttributeData(trade.MeshAttribute.NORMAL, VertexFormat.VECTOR3, containers.StridedArrayView1D(a).expanded(0, (2, 3)), morph_target_id=37)
b_refcount = sys.getrefcount(b)
self.assertEqual(b.name, trade.MeshAttribute.NORMAL)
self.assertEqual(b.format, VertexFormat.VECTOR3)
self.assertEqual(b.array_size, 0)
self.assertEqual(b.morph_target_id, 37)
self.assertIs(b.owner, a)
self.assertEqual(sys.getrefcount(a), a_refcount + 1)
data = b.data
self.assertEqual(data.size, (2, 1))
self.assertEqual(data.stride, (12, 12))
self.assertEqual(data.format, '3f')
self.assertIs(data.owner, a)
# Returns a 2D view always, transpose and take the first element to
# "flatten" it.
self.assertEqual(list(data.transposed(0, 1)[0]), [(1.0, 0.0, 0.0), (0.0, -1.0, 0.0)])
# The data reference the original array, not the MeshAttributeData
# instance
self.assertEqual(sys.getrefcount(b), b_refcount)
self.assertEqual(sys.getrefcount(a), a_refcount + 2)
del b
self.assertEqual(sys.getrefcount(a), a_refcount + 1)
del data
self.assertEqual(sys.getrefcount(a), a_refcount)
def test_init_1d_array(self):
data = array.array('Q', [0x0000ffff66663333, 0x00009999aaaacccc, 0x0000bbbb22227777, 0x00001111eeee8888])
a = trade.MeshAttributeData(trade.MeshAttribute.JOINT_IDS, VertexFormat.UNSIGNED_SHORT, data, array_size=3)
self.assertEqual(a.name, trade.MeshAttribute.JOINT_IDS)
self.assertEqual(a.format, VertexFormat.UNSIGNED_SHORT)
self.assertEqual(a.array_size, 3)
self.assertEqual(a.morph_target_id, -1)
data = a.data
self.assertEqual(data.size, (4, 3))
self.assertEqual(data.stride, (8, 2))
self.assertEqual(data.format, 'H')
# Getting all first, second and third array elements. Assumes Little
# Endian.
self.assertEqual(list(data.transposed(0, 1)[0]), [0x3333, 0xcccc, 0x7777, 0x8888])
self.assertEqual(list(data.transposed(0, 1)[1]), [0x6666, 0xaaaa, 0x2222, 0xeeee])
self.assertEqual(list(data.transposed(0, 1)[2]), [0xffff, 0x9999, 0xbbbb, 0x1111])
def test_init_2d_array(self):
data = array.array('H', [0x3333, 0x6666, 0xffff, 0xcccc, 0xaaaa, 0x9999, 0x7777, 0x2222, 0xbbbb, 0x8888, 0xeeee, 0x1111])
a = trade.MeshAttributeData(trade.MeshAttribute.JOINT_IDS, VertexFormat.UNSIGNED_SHORT, containers.StridedArrayView1D(data).expanded(0, (4, 3)), array_size=3)
self.assertEqual(a.name, trade.MeshAttribute.JOINT_IDS)
self.assertEqual(a.format, VertexFormat.UNSIGNED_SHORT)
self.assertEqual(a.array_size, 3)
self.assertEqual(a.morph_target_id, -1)
data = a.data
self.assertEqual(data.size, (4, 3))
self.assertEqual(data.stride, (6, 2))
self.assertEqual(data.format, 'H')
# Getting all first, second and third array elements
self.assertEqual(list(data.transposed(0, 1)[0]), [0x3333, 0xcccc, 0x7777, 0x8888])
self.assertEqual(list(data.transposed(0, 1)[1]), [0x6666, 0xaaaa, 0x2222, 0xeeee])
self.assertEqual(list(data.transposed(0, 1)[2]), [0xffff, 0x9999, 0xbbbb, 0x1111])
def test_init_1d_invalid(self):
data = array.array('Q', [0, 0, 0])
data_byte = array.array('B', [0, 0, 0])
# To check that messages properly handle the case of no format string
data_byte_no_format = containers.ArrayView(data_byte)
self.assertEqual(containers.StridedArrayView1D(data_byte_no_format).format, None)
with self.assertRaisesRegex(AssertionError, "VertexFormat.UNSIGNED_INT is not a valid format for MeshAttribute.POSITION"):
trade.MeshAttributeData(trade.MeshAttribute.POSITION, VertexFormat.UNSIGNED_INT, data)
with self.assertRaisesRegex(AssertionError, "data type Q has 8 bytes but VertexFormat.MATRIX3X3B_NORMALIZED expects at least 9"):
trade.MeshAttributeData(trade.MeshAttribute.CUSTOM(57), VertexFormat.MATRIX3X3B_NORMALIZED, data)
with self.assertRaisesRegex(AssertionError, "data type Q has 8 bytes but array of 3 VertexFormat.VECTOR3UB expects at least 9"):
trade.MeshAttributeData(trade.MeshAttribute.CUSTOM(57), VertexFormat.VECTOR3UB, data, array_size=3)
with self.assertRaisesRegex(AssertionError, "data type B has 1 bytes but VertexFormat.UNSIGNED_SHORT expects at least 2"):
trade.MeshAttributeData(trade.MeshAttribute.OBJECT_ID, VertexFormat.UNSIGNED_SHORT, data_byte_no_format)
with self.assertRaisesRegex(AssertionError, "expected vertex count to fit into 32 bits but got 4294967296"):
trade.MeshAttributeData(trade.MeshAttribute.POSITION, VertexFormat.VECTOR3B, containers.StridedArrayView1D(data)[:1].broadcasted(0, 0x100000000))
with self.assertRaisesRegex(AssertionError, "expected stride to fit into 16 bits but got 32768"):
trade.MeshAttributeData(trade.MeshAttribute.POSITION, VertexFormat.VECTOR3B, containers.StridedArrayView1D(data)[::4096])
with self.assertRaisesRegex(AssertionError, "expected stride to fit into 16 bits but got -32769"):
trade.MeshAttributeData(trade.MeshAttribute.OBJECT_ID, VertexFormat.UNSIGNED_BYTE, containers.StridedArrayView1D(data_byte)[::32769].flipped(0))
with self.assertRaisesRegex(AssertionError, "MeshAttribute.POSITION can't be an array attribute"):
trade.MeshAttributeData(trade.MeshAttribute.POSITION, VertexFormat.VECTOR3B, data, array_size=2)
with self.assertRaisesRegex(AssertionError, "MeshAttribute.JOINT_IDS has to be an array attribute"):
trade.MeshAttributeData(trade.MeshAttribute.JOINT_IDS, VertexFormat.UNSIGNED_INT, data)
with self.assertRaisesRegex(AssertionError, "expected morph target ID to be either -1 or less than 128 but got -2"):
trade.MeshAttributeData(trade.MeshAttribute.POSITION, VertexFormat.VECTOR3B, data, morph_target_id=-2)
with self.assertRaisesRegex(AssertionError, "expected morph target ID to be either -1 or less than 128 but got 128"):
trade.MeshAttributeData(trade.MeshAttribute.POSITION, VertexFormat.VECTOR3B, data, morph_target_id=128)
with self.assertRaisesRegex(AssertionError, "morph target not allowed for MeshAttribute.OBJECT_ID"):
trade.MeshAttributeData(trade.MeshAttribute.OBJECT_ID, VertexFormat.UNSIGNED_INT, data, morph_target_id=3)
def test_init_2d_invalid(self):
data = containers.StridedArrayView1D(array.array('I', [0, 0, 0, 0, 0, 0])).expanded(0, (3, 2))
data_byte = containers.StridedArrayView1D(array.array('B', [0, 0, 0])).expanded(0, (3, 1))
# To check that messages properly handle the case of no format string
data_byte_no_format = containers.StridedArrayView1D(containers.ArrayView(array.array('B', [0, 0, 0]))).expanded(0, (3, 1))
self.assertEqual(data_byte_no_format.format, None)
with self.assertRaisesRegex(AssertionError, "VertexFormat.UNSIGNED_INT is not a valid format for MeshAttribute.POSITION"):
trade.MeshAttributeData(trade.MeshAttribute.POSITION, VertexFormat.UNSIGNED_INT, data)
with self.assertRaisesRegex(AssertionError, "2-item second dimension of data type I has 8 bytes but VertexFormat.MATRIX3X3B_NORMALIZED expects at least 9"):
trade.MeshAttributeData(trade.MeshAttribute.CUSTOM(57), VertexFormat.MATRIX3X3B_NORMALIZED, data)
with self.assertRaisesRegex(AssertionError, "2-item second dimension of data type I has 8 bytes but array of 3 VertexFormat.VECTOR3UB expects at least 9"):
trade.MeshAttributeData(trade.MeshAttribute.CUSTOM(57), VertexFormat.VECTOR3UB, data, array_size=3)
with self.assertRaisesRegex(AssertionError, "1-item second dimension of data type B has 1 bytes but VertexFormat.UNSIGNED_SHORT expects at least 2"):
trade.MeshAttributeData(trade.MeshAttribute.OBJECT_ID, VertexFormat.UNSIGNED_SHORT, data_byte_no_format)
with self.assertRaisesRegex(AssertionError, "second view dimension is not contiguous"):
trade.MeshAttributeData(trade.MeshAttribute.POSITION, VertexFormat.VECTOR3UB, data[::1,::2])
with self.assertRaisesRegex(AssertionError, "expected vertex count to fit into 32 bits but got 4294967296"):
trade.MeshAttributeData(trade.MeshAttribute.POSITION, VertexFormat.VECTOR3B, data[:1].broadcasted(0, 0x100000000))
with self.assertRaisesRegex(AssertionError, "expected stride to fit into 16 bits but got 32768"):
trade.MeshAttributeData(trade.MeshAttribute.POSITION, VertexFormat.VECTOR3B, data[::4096])
with self.assertRaisesRegex(AssertionError, "expected stride to fit into 16 bits but got -32769"):
trade.MeshAttributeData(trade.MeshAttribute.OBJECT_ID, VertexFormat.UNSIGNED_BYTE, data_byte[::32769].flipped(0))
with self.assertRaisesRegex(AssertionError, "MeshAttribute.POSITION can't be an array attribute"):
trade.MeshAttributeData(trade.MeshAttribute.POSITION, VertexFormat.VECTOR3B, data, array_size=2)
with self.assertRaisesRegex(AssertionError, "MeshAttribute.JOINT_IDS has to be an array attribute"):
trade.MeshAttributeData(trade.MeshAttribute.JOINT_IDS, VertexFormat.UNSIGNED_INT, data)
with self.assertRaisesRegex(AssertionError, "expected morph target ID to be either -1 or less than 128 but got -2"):
trade.MeshAttributeData(trade.MeshAttribute.POSITION, VertexFormat.VECTOR3B, data, morph_target_id=-2)
with self.assertRaisesRegex(AssertionError, "expected morph target ID to be either -1 or less than 128 but got 128"):
trade.MeshAttributeData(trade.MeshAttribute.POSITION, VertexFormat.VECTOR3B, data, morph_target_id=128)
with self.assertRaisesRegex(AssertionError, "morph target not allowed for MeshAttribute.OBJECT_ID"):
trade.MeshAttributeData(trade.MeshAttribute.OBJECT_ID, VertexFormat.UNSIGNED_INT, data, morph_target_id=3)
def test_data_access_unsupported_format(self):
data = array.array('Q', [0, 0, 0])
a = trade.MeshAttributeData(trade.MeshAttribute.CUSTOM(57), VertexFormat.MATRIX3X2B_NORMALIZED, data)
with self.assertRaisesRegex(NotImplementedError, "access to VertexFormat.MATRIX3X2B_NORMALIZED is not implemented yet, sorry"):
a.data
class MeshData(unittest.TestCase):
def test_custom_attribute(self):
# Creating a custom attribute

93
src/python/magnum/trade.cpp

@ -569,6 +569,39 @@ py::object materialAttribute(const Trade::MaterialData& material, const Unsigned
CORRADE_INTERNAL_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
void meshAttributeDataConstructorChecks(const Trade::MeshAttribute name, const VertexFormat format, const Containers::StridedArrayView1D<const void>& data, const UnsignedShort arraySize, const Int morphTargetId) {
if(!Trade::Implementation::isVertexFormatCompatibleWithAttribute(name, format)) {
PyErr_Format(PyExc_AssertionError, "%S is not a valid format for %S", py::cast(format).ptr(), py::cast(name).ptr());
throw py::error_already_set{};
}
#ifndef CORRADE_TARGET_32BIT
if(data.size() > 0xffffffffull) {
PyErr_Format(PyExc_AssertionError, "expected vertex count to fit into 32 bits but got %zu", data.size());
throw py::error_already_set{};
}
#endif
if(data.stride() < -32768 || data.stride() > 32767) {
PyErr_Format(PyExc_AssertionError, "expected stride to fit into 16 bits but got %zi", data.stride());
throw py::error_already_set{};
}
if(morphTargetId < -1 || morphTargetId >= 128) {
PyErr_Format(PyExc_AssertionError, "expected morph target ID to be either -1 or less than 128 but got %i", morphTargetId);
throw py::error_already_set{};
}
if(morphTargetId != -1 && !Trade::Implementation::isMorphTargetAllowed(name)) {
PyErr_Format(PyExc_AssertionError, "morph target not allowed for %S", py::cast(name).ptr());
throw py::error_already_set{};
}
if(arraySize != 0 && !Trade::Implementation::isAttributeArrayAllowed(name)) {
PyErr_Format(PyExc_AssertionError, "%S can't be an array attribute", py::cast(name).ptr());
throw py::error_already_set{};
}
if(arraySize == 0 && Trade::Implementation::isAttributeArrayExpected(name)) {
PyErr_Format(PyExc_AssertionError, "%S has to be an array attribute", py::cast(name).ptr());
throw py::error_already_set{};
}
}
Containers::Triple<const char*, py::object(*)(const char*), void(*)(char*, py::handle)> accessorsForMeshIndexType(const MeshIndexType type) {
switch(type) {
#define _c(type) \
@ -931,6 +964,66 @@ void trade(py::module_& m) {
.value("OBJECT_ID", Trade::MeshAttribute::ObjectId);
enumWithCustomValues<Trade::MeshAttribute, Trade::Implementation::MeshAttributeCustom>(meshAttribute);
py::class_<Trade::MeshAttributeData, Trade::PyDataHolder<Trade::MeshAttributeData>>{m, "MeshAttributeData", "Mesh attribute data"}
.def(py::init([](Trade::MeshAttribute name, VertexFormat format, const Containers::PyStridedArrayView<1, const char>& data, UnsignedShort arraySize, Int morphTargetId) {
const UnsignedInt formatSize = vertexFormatSize(format)*(arraySize ? arraySize : 1);
if(data.itemsize < formatSize) {
const char* const dataFormat = data.format ? data.format.data() : "B";
if(arraySize)
PyErr_Format(PyExc_AssertionError, "data type %s has %zu bytes but array of %u %S expects at least %u", dataFormat, data.itemsize, UnsignedInt(arraySize), py::cast(format).ptr(), formatSize);
else
PyErr_Format(PyExc_AssertionError, "data type %s has %zu bytes but %S expects at least %u", dataFormat, data.itemsize, py::cast(format).ptr(), formatSize);
throw py::error_already_set{};
}
meshAttributeDataConstructorChecks(name, format, data, arraySize, morphTargetId);
return Trade::pyDataHolder(Trade::MeshAttributeData{name, format, Containers::StridedArrayView1D<const void>{data}, arraySize, morphTargetId}, pyObjectHolderFor<Containers::PyArrayViewHolder>(data).owner);
}), "Construct from a 1D view", py::arg("name"), py::arg("format"), py::arg("data"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
py::kw_only{}, /* new in pybind11 2.6 */
#endif
py::arg("array_size") = 0, py::arg("morph_target_id") = -1)
.def(py::init([](Trade::MeshAttribute name, VertexFormat format, const Containers::PyStridedArrayView<2, const char>& data, UnsignedShort arraySize, Int morphTargetId) {
if(data.itemsize != std::size_t(data.stride()[1])) {
PyErr_SetString(PyExc_AssertionError, "second view dimension is not contiguous");
throw py::error_already_set{};
}
const std::size_t secondDimensionSize = data.itemsize*data.size()[1];
const UnsignedInt formatSize = vertexFormatSize(format)*(arraySize ? arraySize : 1);
if(secondDimensionSize < formatSize) {
const char* const dataFormat = data.format ? data.format.data() : "B";
if(arraySize)
PyErr_Format(PyExc_AssertionError, "%zu-item second dimension of data type %s has %zu bytes but array of %u %S expects at least %u", data.size()[1], dataFormat, secondDimensionSize, UnsignedInt(arraySize), py::cast(format).ptr(), formatSize);
else
PyErr_Format(PyExc_AssertionError, "%zu-item second dimension of data type %s has %zu bytes but %S expects at least %u", data.size()[1], dataFormat, secondDimensionSize, py::cast(format).ptr(), formatSize);
throw py::error_already_set{};
}
/* All checks on the second dimension are done now, drop it */
const Containers::StridedArrayView1D<const void> data1D = data.transposed<0, 1>()[0];
meshAttributeDataConstructorChecks(name, format, data1D, arraySize, morphTargetId);
return Trade::pyDataHolder(Trade::MeshAttributeData{name, format, data1D, arraySize, morphTargetId}, pyObjectHolderFor<Containers::PyArrayViewHolder>(data).owner);
}), "Construct from a 2D view", py::arg("name"), py::arg("format"), py::arg("data"),
#if PYBIND11_VERSION_MAJOR*100 + PYBIND11_VERSION_MINOR >= 206
py::kw_only{}, /* new in pybind11 2.6 */
#endif
py::arg("array_size") = 0, py::arg("morph_target_id") = -1)
.def_property_readonly("name", &Trade::MeshAttributeData::name, "Attribute name")
.def_property_readonly("format", &Trade::MeshAttributeData::format, "Attribute format")
.def_property_readonly("array_size", &Trade::MeshAttributeData::arraySize, "Attribute array size")
.def_property_readonly("morph_target_id", &Trade::MeshAttributeData::morphTargetId, "Morph target ID")
.def_property_readonly("data", [](const Trade::MeshAttributeData& self) {
const Containers::Triple<const char*, py::object(*)(const char*), void(*)(char*, py::handle)> formatStringGetitemSetitem = accessorsForVertexFormat(self.format());
if(!formatStringGetitemSetitem.first()) {
PyErr_Format(PyExc_NotImplementedError, "access to %S is not implemented yet, sorry", py::cast(self.format()).ptr());
throw py::error_already_set{};
}
const std::size_t formatSize = vertexFormatSize(self.format());
return Containers::pyArrayViewHolder(Containers::PyStridedArrayView<2, const char>{Containers::arrayCast<2, const char>(self.data(), formatSize*(self.arraySize() ? self.arraySize() : 1)).every({1, formatSize}), formatStringGetitemSetitem.first(), formatSize, formatStringGetitemSetitem.second(), formatStringGetitemSetitem.third()}, pyObjectHolderFor<Trade::PyDataHolder>(self).owner);
}, "Attribute data")
.def_property_readonly("owner", [](Trade::MeshAttributeData& self) {
return pyObjectHolderFor<Trade::PyDataHolder>(self).owner;
}, "Memory owner");
py::class_<Trade::MeshData, Trade::PyDataHolder<Trade::MeshData>>{m, "MeshData", "Mesh data"}
.def_property_readonly("primitive", &Trade::MeshData::primitive, "Primitive")
.def_property_readonly("index_data_flags", [](const Trade::MeshData& self) {

Loading…
Cancel
Save