GitHub Release PyPI - Version

blempy — Blender ↔ NumPy helpers

blempy provides small, safe utilities to efficiently transfer Blender property-collection attributes (e.g. vertex coordinates) to/from NumPy arrays and perform vectorized operations with minimal Python overhead.

[!NOTE]
This module is not fully fleshed out yet, and its contents/interface may change

What problem are we trying to solve?

If we want to access an attribute layer and work with the data in an efficient way on large meshes, we need to do a couple of things:

And although well worth it for large meshes ( see here ), it is also a lot of boiler plate we have to repeat for every attribute layer we want to access, something that blempy can abstract away.

Now blempy also utilizes Blender's foreach_get()/foreach_set() to access attributes as Numpy arrays, but does away with much of the boilerplate by figuring out array dimensions and providing convenient iterators and helper functions, even for attributes that are associated with loops (a.k.a. face corners).

Example: Assuming mesh is a bpy.types.Mesh object that has a vertex color layer called "Color", scaling all rgb components of those vertex colors by half reduces to a few lines of code:

proxy = blempy.UnifiedAttribute(mesh, "Color") proxy.get() for polygon_loops in proxy: polygon_loops[:,:3] *= 0.5 proxy.set()

Key classes

This module contains several utility classes which allow for efficient manipulation of property collections.

The class PropertyCollection takes care of allocating suitably sized and shaped numpy ndarrays when getting attributes from a property collection and can also copy those values back.

The class UnifiedAttribute is designed to deal with unified attribute layers, including those in the CORNER domain. These so called loop layers are associated with, but separated from, faces and indexed using loop indices stored with a face. These loop indices are property collections too, but this is all dealt with transparently.

Both classes behave like lists: they can be iterated over and allow index based access. The key difference between the two classes is that the UnifiedAttribute class will iterate over polygons (faces) if it dealing with an attribute in the CORNER domain and return a reference to an array of attributes, otherwise it will behave like a regular PropertyCollection and return a reference to a single attribute.

The references that will be returned are always numpy arrays, for the uv coordinates of a single face with 4 vertices, an ndarray with shape (4,2) will be returned, whereas for the hide attribute of a face, an ndarray with shape (1,) will be returned. All those references are views , not copies, so can be assigned to.

Both classes also provide utility functions to work with properties that are vectors, for example they have methods to convert between 3D and 4D vectors as well as a matmul method for matrix multiplication with the @ operator, which will apply the matrix multiplication to the whole collection of vector properties at once.

Minimal usage examples

Transforming all vertex coordinates:

from mathutils import Matrix from bpy.context import active_object from blempy import PropertyCollection mesh = active_object.data vproxy = PropertyCollection(mesh, "vertices", "co") vproxy.get() # load vertex coordinates into vproxy.ndarray vproxy.extend() # convert to 4D so that matrix multiplication # can deal with translation too matrix = Matrix.Rotation(pi/4, 4, [0,0,1]) # combine a rotation and matrix = matrix @ Matrix.Translation(4, [0,0,1]) # a translation into a single matrix vproxy = vproxy @= matrix # transform in-place vproxy.discard() # discard the 4th column vproxy.set() # write back to mesh

Give all faces of a mesh a uniform but unique random greyscale color:

from random import random from bpy.context import active_object from blempy import UnifiedAttribute mesh = active_object.data # assume the mesh already has a vertex color layer called "Color" proxy = blempy.UnifiedAttribute(mesh, "Color") # iterate over faces and set all loops in each individual face to a distinct grey level # setting all loops to the same value will cause the face to have a uniform color # NOTE: no need for a proxy.get() call, we will replace all data so we don´t need the originals for index, polygon_loops in enumerate(proxy): grey_level = random() polygon_loops[:] = [grey_level, grey_level, grey_level, 1.0] # sync data back to the mesh proxy.set()

Installation

blempy is available as a package on pypi and can be installed in the usual way:

python -m pip install blempy

But that would install the package in the default location. That is fine if you use Blender as a module, but if your are developing an add-on that you want to bundle and distribute to others, it is probably best to install it right into the folder of the add-on you are developing:

cd your-add-on python -m pip install -t . blempy

If for some reason you want to install blempy inside your Blender environment, then things are a bit more complicated because you will need to find out where the python that is bundled with Blender is and then install it there. A refresher on how to do that can be found in this old article , but think twice before you decide to do that because you will have to repeat this every time you install a new version of Blender.

TODO