Blender Python Mesh Data Access Cheat Sheet

I will summarize how to access mesh data with Blender Python. It's not that hard to look up, but I don't have a lot of information, so I'll put it together so that it can be listed as a cheat sheet. There is no guarantee that the methods described here are the best for speed. Please let me know if there is any other better way.

Below, I would like to summarize the sample code that creates cubes and sets them with UV, Vertex Color, Shape key, and Skin Weight (Vertex Group). It also contains the code and log to print the data set in them.

Postscript (2019/04/03): The version of Blender became 2.80, and the API was partially changed and stopped working, so I fixed it. Basically, the following 4 points.

  1. When new data such as Object, Mesh, etc., the name keyword of the argument can no longer be omitted, so it is specified. Also, object_data.
  2. Due to the increased concept of collection, I modified bpy.context.scene.objects to bpy.context.scene.collection.objects.
  3. Since mesh.uv_textures can no longer be used, I changed it to uv_layers.
  4. Since alpha cannot be omitted in the initialization of the color class, it has been modified so that alpha is also specified. I modified the source code below and commented out the code so far.

Creating a mesh

You can also create a mesh by entering edit mode and creating it as a BMesh, but here's how to create it in object mode.

create_mesh.py


import bpy

verts = [[-1.0, -1.0,  1.0], [1.0, -1.0,  1.0], [1.0, 1.0,  1.0], [-1.0, 1.0,  1.0],
         [-1.0, -1.0, -1.0], [1.0, -1.0, -1.0], [1.0, 1.0, -1.0], [-1.0, 1.0, -1.0], ]
faces = [[0,1,2,3], [0,4,5,1], [1,5,6,2], [2,6,7,3], [0,3,7,4], [4,7,6,5]]

msh = bpy.data.meshes.new(name="cubemesh")
#msh = bpy.data.meshes.new("cubemesh")
msh.from_pydata(verts, [], faces)
msh.update()
obj = bpy.data.objects.new(name="cube", object_data=msh)
#obj = bpy.data.objects.new("cube", msh)
scene = bpy.context.scene
#scene.objects.link(obj)
scene.collection.objects.link(obj)

As a flow,

  1. Create an instance of Mesh data with meshes.new
  2. Create a mesh by registering the vertex coordinates and the array of vertex indexes of each face with from_pydata.
  3. Reflect the mesh changes with msh.update (). It may not be necessary if you are calling from_pydata.
  4. Create an Object with ʻobjects.new`, but pass the mesh data created earlier as an argument.
  5. Finally, don't forget to link the objects created in scene.objects.link to the scene.

The [] part of the central argument of from_pydata sets the edge information. If not, it will be calculated automatically from the Face information, so there is no problem with an empty array when creating a mesh that generates faces. Blender can also create a mesh without faces, in which case you can specify edge and empty the Face Index specification. You may want to script a mesh without faces, such as a mesh for a rig controller.

Access to vertex data

--vertices property --index: Index of vertices --co: Coordinates of vertices --normal: Vertex normal

#Enumeration of vertex information
print("num of vertices:", len(msh.vertices))
for vt in msh.vertices:
    print("vertex index:{0:2} co:{1} normal:{2}".format(vt.index, vt.co, vt.normal))

output


num of vertices: 8
vertex index: 0 co:<Vector (-1.0000, -1.0000, 1.0000)> normal:<Vector (-0.5773, -0.5773, 0.5773)>
vertex index: 1 co:<Vector (1.0000, -1.0000, 1.0000)> normal:<Vector (0.5773, -0.5773, 0.5773)>
vertex index: 2 co:<Vector (1.0000, 1.0000, 1.0000)> normal:<Vector (0.5773, 0.5773, 0.5773)>
vertex index: 3 co:<Vector (-1.0000, 1.0000, 1.0000)> normal:<Vector (-0.5773, 0.5773, 0.5773)>
vertex index: 4 co:<Vector (-1.0000, -1.0000, -1.0000)> normal:<Vector (-0.5773, -0.5773, -0.5773)>
vertex index: 5 co:<Vector (1.0000, -1.0000, -1.0000)> normal:<Vector (0.5773, -0.5773, -0.5773)>
vertex index: 6 co:<Vector (1.0000, 1.0000, -1.0000)> normal:<Vector (0.5773, 0.5773, -0.5773)>
vertex index: 7 co:<Vector (-1.0000, 1.0000, -1.0000)> normal:<Vector (-0.5773, 0.5773, -0.5773)>

Access to the edge

--edges property --index: Index of the edge --vertices: Index of adjacent vertices

print("num of edges:", len(msh.edges))
for ed in msh.edges:
    print("edge index:{0: 2} v0:{0} v1:{1}".format(ed.index, ed.vertices[0], ed.vertices[1]))

output


num of edges: 12
edge index: 0 v0: 4 v1: 5
edge index: 1 v0: 3 v1: 7
edge index: 2 v0: 6 v1: 7
edge index: 3 v0: 2 v1: 6
edge index: 4 v0: 5 v1: 6
edge index: 5 v0: 0 v1: 1
edge index: 6 v0: 4 v1: 7
edge index: 7 v0: 1 v1: 2
edge index: 8 v0: 1 v1: 5
edge index: 9 v0: 2 v1: 3
edge index:10 v0: 0 v1: 3
edge index:11 v0: 0 v1: 4

Face access

The ** polygons ** property, not the faces. There are more software with the property name faces, and I'm just trying to access it with faces? I emphasize it because it becomes.

--polygons property --index: Polygon Index --vertices: Index of adjacent vertices --loop_start: Index of start loop data to iterate the edges and vertices of this polygon (Mesh has a loops property and Index to it) --loop_total: Number of loops, real, number of polygon vertices --loop_indices: List of loop indexes

Get


print("num of polygons:", len(msh.polygons))
for pl in msh.polygons:
    print("polygon index:{0:2} ".format(pl.index), end="")
    print("vertices:", end="")
    for vi in pl.vertices:
        print("{0:2}, ".format(vi), end="")
    print("")

for pl in msh.polygons:
    print("polygon index:{0:2} ".format(pl.index))
    print(" > loops:", end="")
    print(" total:", pl.loop_total, end="")
    print(" start:", pl.loop_start, end="")
    print(" indices:", end="")
    for lp in pl.loop_indices:
        print("{0:2}, ".format(lp), end="")
    print("")

output


num of polygons: 6
polygon index: 0 vertices: 0,  1,  2,  3,
polygon index: 1 vertices: 0,  4,  5,  1,
polygon index: 2 vertices: 1,  5,  6,  2,
polygon index: 3 vertices: 2,  6,  7,  3,
polygon index: 4 vertices: 0,  3,  7,  4,
polygon index: 5 vertices: 4,  7,  6,  5,
polygon index: 0
 > loops: total: 4 start: 0 indices: 0,  1,  2,  3,
polygon index: 1
 > loops: total: 4 start: 4 indices: 4,  5,  6,  7,
polygon index: 2
 > loops: total: 4 start: 8 indices: 8,  9, 10, 11,
polygon index: 3
 > loops: total: 4 start: 12 indices:12, 13, 14, 15,
polygon index: 4
 > loops: total: 4 start: 16 indices:16, 17, 18, 19,
polygon index: 5
 > loops: total: 4 start: 20 indices:20, 21, 22, 23,

About Loop

Data that manages the indexes of vertices and edges. It can be used when you want to trace the edges around a polygon. However, if you want to trace the adjacency of vertices, edges, and polygons properly, it is better to switch to Edit mode and use Bmesh, so I think that there is such a property.

print("num of loops:", len(msh.loops))
for lp in msh.loops:
    print("loop index:{0:2} vertex index:{1:2} edge index:{2:2}".format(lp.index, lp.vertex_index, lp.edge_index))

output


num of loops: 24
loop index: 0 vertex index: 0 edge index: 5
loop index: 1 vertex index: 1 edge index: 7
loop index: 2 vertex index: 2 edge index: 9
loop index: 3 vertex index: 3 edge index:10
loop index: 4 vertex index: 0 edge index:11
loop index: 5 vertex index: 4 edge index: 0
loop index: 6 vertex index: 5 edge index: 8
loop index: 7 vertex index: 1 edge index: 5
loop index: 8 vertex index: 1 edge index: 8
loop index: 9 vertex index: 5 edge index: 4
loop index:10 vertex index: 6 edge index: 3
loop index:11 vertex index: 2 edge index: 7
loop index:12 vertex index: 2 edge index: 3
loop index:13 vertex index: 6 edge index: 2
loop index:14 vertex index: 7 edge index: 1
loop index:15 vertex index: 3 edge index: 9
loop index:16 vertex index: 0 edge index:10
loop index:17 vertex index: 3 edge index: 1
loop index:18 vertex index: 7 edge index: 6
loop index:19 vertex index: 4 edge index:11
loop index:20 vertex index: 4 edge index: 6
loop index:21 vertex index: 7 edge index: 2
loop index:22 vertex index: 6 edge index: 4
loop index:23 vertex index: 5 edge index: 0

UV settings

Setting and getting UVs is as simple as adding a UV channel and using that channel as a key to access the UV array. Note that the channels displayed in UV Maps of the GUI are managed by the property called uv_textures of Mesh, but the actual UV coordinate information is held by the property called uv_layers.

It does not maintain a different topology for each UV channel like 3ds Max. Therefore, the number of UV coordinates held in each layer is always the sum of the number of vertices of all polygons. Blender doesn't seem to have the concept of weld, welding in the data.

>>> len(msh.uv_layers[channel_name].data.items())
24
#UV settings
tmp = [[0.0, 0.0], [0.5, 0.0], [0.5, 0.5], [0.0, 0.5]]
uvs = tmp * 6 #Initialize UV coordinates to set
channel_name = "uv0" #UV channel name
msh.uv_layers.new(name=channel_name) #Creating a UV Channel
#msh.uv_textures.new(channel_name) #Creating a UV Channel
for idx, dat in enumerate(msh.uv_layers[channel_name].data): #Iterate with an array of uv layers
    dat.uv = uvs[idx]

Data confirmation


print("num of uv layers:", len(msh.uv_layers))
#print("num of uv layers:", len(msh.uv_textures))
for ly in msh.uv_layerss:
    #for ly in msh.uv_textures:
    print(ly.name)
    for idx, dat in enumerate(msh.uv_layers[ly.name].data):
        print("  {0}:{1}".format(idx, dat.uv))
    print("")

output


uv0
  0:<Vector (0.0000, 0.0000)>
  1:<Vector (0.5000, 0.0000)>
  2:<Vector (0.5000, 0.5000)>
  3:<Vector (0.0000, 0.5000)>
  4:<Vector (0.0000, 0.0000)>
  5:<Vector (0.5000, 0.0000)>
  6:<Vector (0.5000, 0.5000)>
  7:<Vector (0.0000, 0.5000)>
  8:<Vector (0.0000, 0.0000)>
  9:<Vector (0.5000, 0.0000)>
  10:<Vector (0.5000, 0.5000)>
  11:<Vector (0.0000, 0.5000)>
  12:<Vector (0.0000, 0.0000)>
  13:<Vector (0.5000, 0.0000)>
  14:<Vector (0.5000, 0.5000)>
  15:<Vector (0.0000, 0.5000)>
  16:<Vector (0.0000, 0.0000)>
  17:<Vector (0.5000, 0.0000)>
  18:<Vector (0.5000, 0.5000)>
  19:<Vector (0.0000, 0.5000)>
  20:<Vector (0.0000, 0.0000)>
  21:<Vector (0.5000, 0.0000)>
  22:<Vector (0.5000, 0.5000)>
  23:<Vector (0.0000, 0.5000)>

Vertex Color

It is easily accessible via the vertex_colors property.

Setting


#6 sides Red, Green, Blue, Cyan, Magenta,Try painting with Yellow
#colormaps = [[1.0,0.0,0.0]]*4+[[0.0,1.0,0.0]]*4+[[0.0,0.0,1.0]]*4+[[0.0,1.0,1.0]]*4+[[1.0,0.0,1.0]]*4+[[1.0,1.0,0.0]]*4
colormaps = [[1.0,0.0,0.0,1.0]]*4+[[0.0,1.0,0.0,1.0]]*4+[[0.0,0.0,1.0,1.0]]*4+[[0.0,1.0,1.0,1.0]]*4+[[1.0,0.0,1.0,1.0]]*4+[[1.0,1.0,0.0,1.0]]*4

print("colormaps:", colormaps)
msh.vertex_colors.new(name='col')
# msh.vertex_colors.new('col')
for idx, vc in enumerate(msh.vertex_colors['col'].data):
    vc.color = colormaps[idx]

Data acquisition


#Display of Vertex Color
print("num of vertex color layers:", len(msh.vertex_colors))
for ly in msh.vertex_colors:
    print(ly.name)
    for idx, vc in enumerate(msh.vertex_colors['col'].data):
        print("  {0:2}:{1}".format(idx,vc.color))

output


num of vertex color layers: 1
col
   0:<Color (r=1.0000, g=0.0000, b=0.0000)>
   1:<Color (r=1.0000, g=0.0000, b=0.0000)>
   2:<Color (r=1.0000, g=0.0000, b=0.0000)>
   3:<Color (r=1.0000, g=0.0000, b=0.0000)>
   4:<Color (r=0.0000, g=1.0000, b=0.0000)>
   5:<Color (r=0.0000, g=1.0000, b=0.0000)>
   6:<Color (r=0.0000, g=1.0000, b=0.0000)>
   7:<Color (r=0.0000, g=1.0000, b=0.0000)>
   8:<Color (r=0.0000, g=0.0000, b=1.0000)>
   9:<Color (r=0.0000, g=0.0000, b=1.0000)>
  10:<Color (r=0.0000, g=0.0000, b=1.0000)>
  11:<Color (r=0.0000, g=0.0000, b=1.0000)>
  12:<Color (r=0.0000, g=1.0000, b=1.0000)>
  13:<Color (r=0.0000, g=1.0000, b=1.0000)>
  14:<Color (r=0.0000, g=1.0000, b=1.0000)>
  15:<Color (r=0.0000, g=1.0000, b=1.0000)>
  16:<Color (r=1.0000, g=0.0000, b=1.0000)>
  17:<Color (r=1.0000, g=0.0000, b=1.0000)>
  18:<Color (r=1.0000, g=0.0000, b=1.0000)>
  19:<Color (r=1.0000, g=0.0000, b=1.0000)>
  20:<Color (r=1.0000, g=1.0000, b=0.0000)>
  21:<Color (r=1.0000, g=1.0000, b=0.0000)>
  22:<Color (r=1.0000, g=1.0000, b=0.0000)>
  23:<Color (r=1.0000, g=1.0000, b=0.0000)>

Shape Key

It's basically the same as the UV setting. Unlike UVs, the mesh's shape_keys property is responsible for both shape key management and vertex data management. It is the property called key_blocks of the shape_keys property that actually has the data.

Setting



 #In the verts variable defined when creating the Cube(0,0,1), (0,0,-1)MoveUp each,Use the Move Down key
shapemaps = {'MoveUp':[[v[0],v[1],v[2]+1.0] for v in verts],
             'MoveDown':[[v[0],v[1],v[2]-1.0] for v in verts],}

#Create a Basis key if there is no Shape
obj.shape_key_add()
msh.shape_keys.key_blocks[-1].name = "Basis"

for sname in shapemaps:
    lst = shapemaps[sname]
    obj.shape_key_add()
    kb = msh.shape_keys.key_blocks[-1]
    kb.name = sname
    for idx, co in enumerate(lst):
        kb.data[idx].co = co

Data acquisition


#Get Shape Key
print("num of Shape Keys:", len(msh.shape_keys.key_blocks))
for kb in msh.shape_keys.key_blocks:
    print("  Key Block:", kb.name)
    for idx, dat in enumerate(kb.data):
        print("    {0}:{1}".format(idx, dat.co))

output


num of Shape Keys: 3
  Key Block: Basis
    0:<Vector (-1.0000, -1.0000, 1.0000)>
    1:<Vector (1.0000, -1.0000, 1.0000)>
    2:<Vector (1.0000, 1.0000, 1.0000)>
    3:<Vector (-1.0000, 1.0000, 1.0000)>
    4:<Vector (-1.0000, -1.0000, -1.0000)>
    5:<Vector (1.0000, -1.0000, -1.0000)>
    6:<Vector (1.0000, 1.0000, -1.0000)>
    7:<Vector (-1.0000, 1.0000, -1.0000)>
  Key Block: MoveDown
    0:<Vector (-1.0000, -1.0000, 0.0000)>
    1:<Vector (1.0000, -1.0000, 0.0000)>
    2:<Vector (1.0000, 1.0000, 0.0000)>
    3:<Vector (-1.0000, 1.0000, 0.0000)>
    4:<Vector (-1.0000, -1.0000, -2.0000)>
    5:<Vector (1.0000, -1.0000, -2.0000)>
    6:<Vector (1.0000, 1.0000, -2.0000)>
    7:<Vector (-1.0000, 1.0000, -2.0000)>
  Key Block: MoveUp
    0:<Vector (-1.0000, -1.0000, 2.0000)>
    1:<Vector (1.0000, -1.0000, 2.0000)>
    2:<Vector (1.0000, 1.0000, 2.0000)>
    3:<Vector (-1.0000, 1.0000, 2.0000)>
    4:<Vector (-1.0000, -1.0000, 0.0000)>
    5:<Vector (1.0000, -1.0000, 0.0000)>
    6:<Vector (1.0000, 1.0000, 0.0000)>
    7:<Vector (-1.0000, 1.0000, 0.0000)>

Vertex Group

I think it is the simplest data, but it is the most troublesome data structure. Blender holds the Skin Weight in this Vertex Group. Therefore, the data is held as an associative array with the Index of Vertex Group as the key and Weight as the value for each vertex. A function to set the Vertex Weight is provided in the structure of the Vertex Group so that it can be set with a feeling similar to UV. I don't think the processing speed is fast.

Setting


bone_list = ["bone1", "bone2"]
weight_map = {"bone1":[1.0]*4+[0.0]*4, # [1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0],
              "bone2":[0.0]*4+[1.0]*4,
              }

vg_list = [] #List of VertexGroup
#Creating a vertex group
for bname in bone_list:
    obj.vertex_groups.new(name=bname)
    #obj.vertex_groups.new(bname)
    vg_list.append(obj.vertex_groups[-1])

#Settings via Vertex Group
for vg in vg_list:
    weights = weight_map[vg.name]
    for vidx, w in enumerate(weights):
        if w != 0.0:
            vg.add([vidx], w, 'REPLACE')

Try to get the data via the vertex properties.

Data acquisition


#Getting Weight via Vertex's groups property
msh = obj.data
for v in msh.vertices:
    for vge in v.groups:
        print("vindex:{0} group index:{1} weight:{2}".format(v.index, vge.group, vge.weight))

output


vindex:0 group index:0 weight:1.0
vindex:1 group index:0 weight:1.0
vindex:2 group index:0 weight:1.0
vindex:3 group index:0 weight:1.0
vindex:4 group index:1 weight:1.0
vindex:5 group index:1 weight:1.0
vindex:6 group index:1 weight:1.0
vindex:7 group index:1 weight:1.0

Bonus: About foreach_set

Collection data such as the data property of uv_layer has a function called foreach_set, and you can set the value in one shot with an array. It has not been verified whether it is faster than iterating arrays and assigning them one by one. Note that this method will pass an ordinary array that is flattened instead of the array of the above array.

python


tmp = [0.0, 0.0, 0.5, 0.0, 0.5, 0.5, 0.0, 0.5]
uvs = tmp * 6 #Initialize UV coordinates to set
channel_name = "uv0"
msh.uv_textures.new(channel_name)
msh.uv_layers[channel_name].data.foreach_set("uv", uvlist)

python


shapemaps = {'MoveUp':[[v[0],v[1],v[2]+1.0] for v in verts],
             'MoveDown':[[v[0],v[1],v[2]-1.0] for v in verts],}
for sname in shapemaps:
    lst = shapemaps[sname]
    
    lst2 = list(chain.from_iterable(lst)) #This seems to be faster with the method of flattening the array of arrays
    obj.shape_key_add()
    sk = msh.shape_keys.key_blocks[-1]
    sk.name = sname
    sk.data.foreach_set("co", lst2)

Recommended Posts

Blender Python Mesh Data Access Cheat Sheet
Data Science Cheat Sheet (Python)
Python3 cheat sheet (basic)
PySpark Cheat Sheet [Python]
Python sort cheat sheet
[Python3] Standard input [Cheat sheet]
Python Django Tutorial Cheat Sheet
Apache Beam Cheat Sheet [Python]
Python cheat sheet (for C ++ experienced)
Access Blender Shader Nodes from Python
Python Computation Library Cheat Sheet ~ itertools ~
AtCoder cheat sheet in python (for myself)
Mathematical Optimization Modeler (PuLP) Cheat Sheet (Python)
Data analysis python
Blender 2.9 Python Extrude extrude
[Updating] Python Syntax cheat sheet for Java shop
Curry cheat sheet
SQLite3 cheat sheet
pyenv cheat sheet
[python] Read data
[For data science] Oreore Jupyter cheat sheet [Jupyter Notebook / Lab]
Get data from database via ODBC with Python (Access)
Data analysis with python 2
conda command cheat sheet
PIL / Pillow cheat sheet
Linux command cheat sheet
Access bitcoind from python
Data analysis using Python 0
Data analysis overview python
blender, python, spiral staircase
ps command cheat sheet
Data cleaning using Python
Spark API cheat sheet
blender, python, sphere behavior
Blender 2.8, Python Cube Scaling
Operate Blender with Python
Go language cheat sheet
Python data analysis template
[Python tutorial] Data structure
[Python] Sorting Numpy data
Python pdf cheat sheets
[Blender x Python] Blender Python tips (11/100)
Data analysis with Python