Blender Python Tips
访问数据(bpy.data)
objects / meshes / scenes
- 红框为 对象名
- 蓝框为 网格名
import bpy
# 打印所有 对象名
for obj in bpy.data.objects:
print(obj.name)
# 移除 网格名 叫 `Cube` 的网格
if "Cube" in bpy.data.meshes:
mesh = bpy.data.meshes["Cube"]
print("removing mesh", mesh)
bpy.data.meshes.remove(mesh)
# 打印列表中所有 场景 名字
print(bpy.data.scenes.keys())
filepath / images
在 .blend 文件目录下新建一个 .txt,往其中录入项目中的 图像路径 和 尺寸 信息
import os
# 分离 blend 文件名 与 扩展名 ;默认返回 (fname, fextension) 元组,可做分片操作
with open(os.path.splitext(bpy.data.filepath)[0] + ".txt", 'w') as fs:
for image in bpy.data.images:
fs.write("%s %d x %d\n" % (image.filepath, image.size[0], image.size[1]))
按名称选择对象(bpy.ops)
object.select_all
def select(objName):
# 清除其他选择
bpy.ops.object.select_all(action = 'DESELECT')
# 将数据块的 'select' 属性设置为 True
bpy.data.objects[objName].select_set(True)
或者更高级的写法
def mySelector(objName, additive=False):
# 默认情况下,清除其他选择
if not additive:
bpy.ops.object.select_all(action='DESELECT')
# 将数据块的 'select' 属性设置为 True
bpy.data.objects[objName].select_set(True)
输出所选对象的属性(bpy.context)
selected_objects
遍历 bpy.context.selected_objects 输出 bpy.data.objects 数据块
比如输出 [ bpy.data.objects['Sphere'], bpy.data.objects['Circle'], bpy.data.objects['Cube'] ]
# 返回所选对象的名称
k.name for k in bpy.context.selected_objects
# 返回所选对象的位置
k.location for k in bpy.context.selected_objects
变换选择的对象(bpy.ops)
平移、缩放、旋转(分别 x,y,z 轴)
transform
# Differential
def translate(v):
bpy.ops.transform.translate(value = v, constraint_axis = (True, True, True))
# Differential
def scale(v):
bpy.ops.transform.resize(value = v, constraint_axis = (True, True, True))
# Differential
def rotate_x(v):
bpy.ops.transform.rotate(value = v, constraint_axis = (1, 0, 0))
# Differential
def rotate_y(v):
bpy.ops.transform.rotate(value = v, constraint_axis = (0, 1, 0))
# Differential
def rotate_z(v):
bpy.ops.transform.rotate(value = v, constraint_axis = (0, 0, 1))
object.transform_apply
# 将对象的变换应用于其数据
def transform_apply():
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
按名称激活对象(bpy.context)
scene.objects.active (已过时)
def activate(objName):
bpy.context.scene.objects.active = bpy.data.objects[objName]
由于在任何给定时间 只有一个 对象是激活的,因此激活函数要简单得多。我们将bpy.data.objects数据块传递给在激活时处理内部数据的场景属性。因为 Blender 只允许 单个 对象处于活动状态,我们可以对bpy.context.scene进行单个赋值,并允许 Blender 的内部引擎对其他对象的取消激活进行排序。
访问激活对象(bpy.context)
object / active_object
# 返回 bpy.data.objects 数据块
bpy.context.object
相较上面一行的较长些的同义词
bpy.context.active_object
访问 数据块 的 名称 和 位置 属性
bpy.context.object.name
bpy.context.object.location
重命名对象(bpy.context)
重命名激活对象
def rename(objName):
bpy.context.object.name = objName
删除对象(bpy.ops)
object.delete
根据名称删除对象
def delete(objName):
select(objName)
bpy.ops.object.delete(use_global=False)
object.select_all
删除所有对象
def delete_all():
if( len(bpy.data.objects) != 0 ):
bpy.ops.object.select_all(action = 'SELECT')
bpy.ops.object.delete(use_global=False)
设置对象的交互模式(bpy.ops)
object.mode_set
设置对象的交互模式(OBJECT/EDIT)
函数用于进入 编辑模式 而不选择任何顶点,或进入 对象模式 而没有任何其他的处理
def mode(mode_name):
bpy.ops.object.mode_set(mode = mode_name)
if mode_name == "EDIT":
bpy.ops.mesh.select_all(action="DESELECT")
网格编辑模式下选择 点、线、面 模式(bpy.ops)
mesh.select_mode
选择模式: VERT、EDGE 或 FACE
bpy.ops.mesh.select_mode(type = type)
网格的全选/取消全选(bpy.ops)
mesh.select_all
全选
def select_all():
bpy.ops.mesh.select_all(action="SELECT")
取消全选
def deselect_all():
bpy.ops.mesh.select_all(action="DESELECT")
网格挤出(bpy.ops)
mesh.extrude_region_move
挤出区域并移动结果
def extrude(v):
bpy.ops.mesh.extrude_region_move(TRANSFORM_OT_translate =
{"value":v,
"constraint_axis": (True, True, True),
"constraint_orientation":'NORMAL'}) # 沿法线方向
网格细分(bpy.ops)
mesh.subdivide
对所选边进行细分
def subdivide(cuts = 1):
bpy.ops.mesh.subdivide(number_cuts = cuts)
读取网格 UV 数据的简单示例代码
loops / uv_layers
注意 网格环(Mesh Loops)通常比顶点多很多。除非是一个原始的未分割平面的情况
import bpy
# access mesh data:
obj = bpy.context.active_object
mesh_data = obj.data
mesh_loops = mesh_data.loops
uv_index = 0
# iterate teh mesh loops:
for lp in mesh_loops:
# access uv loop:
uv_loop = mesh_data.uv_layers[uv_index].data[lp.index]
uv_coords = uv_loop.uv
# 得到了 顶点索引 和对应 uv 坐标
print('vert: {}, U: {}, V: {}'.format(lp.vertex_index, uv_coords[0], uv_coords[1]))
网格转换(bpy.types)
bpy.types.Mesh -> bmesh.types.BMesh
从当前激活的网格返回一个 BMesh,当前网格 必须 已经处于 编辑 模式。
def register_bmesh():
return bmesh.from_edit_mesh(bpy.context.object.data)
类似的变换是
bmesh.types.BMesh.from_mesh: 从现有的 网格数据块 中初始化这个 bmesh 。bmesh.types.BMesh.from_object: 从现有的 对象数据块 初始化这个 bmesh (目前只支持网格)。
PS: 常常与之搭配的是 bmesh.types.BMVertSeq/bmesh.types.BMEdgeSeq/bmesh.types.BMFaceSeq 的 ensure_lookup_table() 调用。
官方文档中说,该函数的作用是 确保 int 订阅所需的内部数据和 verts /edges/faces 一起初始化,例如 bm.vert[index]。在此序列 添加/删除数据 之后需要再次调用。
或者在使用此序列前进行调用,如
def select_vert(bm, i):
bm.verts.ensure_lookup_table()
bm.verts[i].select = True
def select_edge(bm, e):
bm.edges.ensure_lookup_table()
bm.edges[e].select = True
def select_face(bm, f):
bm.faces.ensure_lookup_table()
bm.faces[f].select = True
我在某本电子书中看到作者声称 —— “鼓励对
ensure_lookup_table()的大量调用。我们使用这些函数来提醒 Blender 保持bmesh对象的某些部分在操作之间 不被垃圾回收。这些函数只占用最小的性能成本,因此我们可以随意调用它们,而不会产生太多后果。”
读取网格动画顶点数据的简单示例(bpy.context)
evaluated_depsgraph_get
注意,访问模型的 动画顶点位置 需要 每帧 读取模型的 评估(变形)网格状态。
因此,使用模型的 更新依赖关系图 depgraph ,每一帧都会初始化一个新的 bmesh 对象。
import bpy
import bmesh
obj = bpy.context.active_object
frames = range(0,10)
# get the object's evaluated dependency graph:
depgraph = bpy.context.evaluated_depsgraph_get()
# iterate animation frames:
for f in frames:
bpy.context.scene.frame_set(f)
# define new bmesh object:
bm = bmesh.new()
bm.verts.ensure_lookup_table()
# read the evaluated mesh data into the bmesh object:
bm.from_object( obj, depgraph )
# iterate the bmesh verts:
for i, v in enumerate(bm.verts):
print("frame: {}, vert: {}, location: {}".format(f, i, v.co))
访问网格的三角形
calc_loop_triangle / loop_triangles / vertices
默认情况下,Blender 中的 Python 无法 访问网格三角形。
当需要访问网格三角形时,它们 必须 首先使用 calc_loop_triangle 网格对象方法进行计算(细分三角形)。
该函数的作用是:从四边形或 NGons 计算三角形细分。
在调用 calc_loop_triangle 方法之前,Mesh 对象的 loop_triangle 属性将引用一个空集合(collection)。
在调用 calc_loop_triangle 方法之后,loop_triangle 属性将引用一个 MeshLoopTriangle 对象集合,其中 vertices 属性将保存一个由 3 个整数组成的数组,它们是三角形顶点的索引。
下面的示例脚本创建并放置球体对象在立方体三角形的中心:
import bpy
mesh = bpy.data.objects['Cube'].data
mesh.calc_loop_triangles()
for tri in mesh.loop_triangles:
tri_center = (mesh.vertices[tri.vertices[0]].co * 0.333) +\
(mesh.vertices[tri.vertices[1]].co * 0.333) +\
(mesh.vertices[tri.vertices[2]].co * 0.333)
bpy.ops.mesh.primitive_uv_sphere_add(radius=0.1,
enter_editmode=False,
location=tri_center)
将一个不可选择的对象设置为本地,使其可以被选中
make_local
如果你碰巧链接了一个在原始 .blend 文件中不可选择的外部对象,你将无法通过交互方式使其再次可选择,或者本地可选择,甚至从当前 .blend 文件中删除它。
好消息是,这可以通过一个非常简短的 Python 脚本轻松完成:
import bpy
obj = bpy.data.objects['the_untouchable_object']
obj.make_local()