The Blender API provides a set of simple property types described in bpy.props (IntProperty, BoolProperty, etc.). But the basic types are not always enough, sometimes we need more complex ones. The Blender API allows to group simple properties to make complex properties.
Let’s consider such complex property creation and make the 3×3 matrix property as an example.
First, let’s define a class with the structure of our property.
A 3×3 matrix can be represented as a combination of three vectors, so we can use the three basic properties of FloatVectorProperty type to describe the matrix:
1 2 3 4 5 6 7 8 9 10 11 12 |
from bpy.types import PropertyGroup class Matrix3x3_property(PropertyGroup): row0: FloatVectorProperty( default=(1.0, 0.0, 0.0) ) row1: FloatVectorProperty( default=(0.0, 1.0, 0.0) ) row2: FloatVectorProperty( default=(0.0, 0.0, 1.0) ) |
Our custom property class must be registered in the register functions and released in the unregister function.
1 2 3 4 5 6 7 8 |
from bpy.utils import register_class, unregister_class def register(): register_class(Matrix3x3_property) def unregister(): unregister_class(Matrix3x3_property) |
To assign our matrix property to objects (meshes) we need to create a pointer to it. Do it also in the register function and clear it in unregister.
1 2 3 4 5 6 7 8 |
from bpy.types import Mesh def register(): Mesh.matrix_prop = PointerProperty(type=Matrix3x3_property) def unregister(): del Mesh.matrix_prop |
Combine all together:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
from bpy.props import PointerProperty, FloatVectorProperty from bpy.types import PropertyGroup, Mesh from bpy.utils import register_class, unregister_class class Matrix3x3_property(PropertyGroup): row0: FloatVectorProperty( default=(1.0, 0.0, 0.0) ) row1: FloatVectorProperty( default=(0.0, 1.0, 0.0) ) row2: FloatVectorProperty( default=(0.0, 0.0, 1.0) ) def register(): register_class(Matrix3x3_property) Mesh.matrix_prop = PointerProperty(type=Matrix3x3_property) def unregister(): del Mesh.matrix_prop unregister_class(Matrix3x3_property) |
We assigned the “matrix_prop” property to each mesh. And we can now access it. For example, to get the value of the first element of the first row of the matrix:
1 2 |
bpy.context.object.data.matrix_prop.row0[0] # 1.0 |
However, it is inconvenient to get the whole matrix that way. Let’s add to the “Matrix3x3_property” class a getter and a setter for convenient getting/setting of its value.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@property def matrix(self): return ( (self.row0[0], self.row0[1], self.row0[2]), (self.row1[0], self.row1[1], self.row1[2]), (self.row2[0], self.row2[1], self.row2[2]) ) @matrix.setter def matrix(self, matrix3x3): self.row0 = matrix3x3[0] self.row1 = matrix3x3[1] self.row2 = matrix3x3[2] |
Now we can get and set our property values in a convenient way.
To set value:
1 |
bpy.context.object.data.matrix_prop.matrix = ((1.0, 1.0, 1.0), (2.0, 2.0, 2.0), (3.0, 3.0, 3.0)) |
and to get it:
1 2 |
bpy.context.object.data.matrix_prop.matrix # ((1.0, 1.0, 1.0), (2.0, 2.0, 2.0), (3.0, 3.0, 3.0)) |
Let’s also define the __repr__ function for beauty printing.
1 2 |
def __repr__(self): return '({0},{1},{2}), ({3},{4},{5}), ({6},{7},{8})'.format(self.row0[0], self.row0[1], self.row0[2], self.row1[0], self.row1[1], self.row1[2], self.row2[0], self.row2[1], self.row2[2]) |
1 2 |
bpy.context.object.data.matrix_prop # (1.0,1.0,1.0), (2.0,2.0,2.0), (3.0,3.0,3.0) |
Final code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
from bpy.props import PointerProperty, FloatVectorProperty from bpy.types import PropertyGroup, Mesh from bpy.utils import register_class, unregister_class class Matrix3x3_property(PropertyGroup): row0: FloatVectorProperty( default=(1.0, 0.0, 0.0) ) row1: FloatVectorProperty( default=(0.0, 1.0, 0.0) ) row2: FloatVectorProperty( default=(0.0, 0.0, 1.0) ) def __repr__(self): return '({0},{1},{2}), ({3},{4},{5}), ({6},{7},{8})'.format(self.row0[0], self.row0[1], self.row0[2], self.row1[0], self.row1[1], self.row1[2], self.row2[0], self.row2[1], self.row2[2]) @property def matrix(self): return ( (self.row0[0], self.row0[1], self.row0[2]), (self.row1[0], self.row1[1], self.row1[2]), (self.row2[0], self.row2[1], self.row2[2]) ) @matrix.setter def matrix(self, matrix3x3): self.row0 = matrix3x3[0] self.row1 = matrix3x3[1] self.row2 = matrix3x3[2] def register(): register_class(Matrix3x3_property) Mesh.matrix_prop = PointerProperty(type=Matrix3x3_property) def unregister(): del Mesh.matrix_prop unregister_class(Matrix3x3_property) |