Blender 脚本之 Operator 初探

时间:2022-06-02 16:39:16

  addon(插件)用来扩展 Blender 的功能,跟其他软件里的 plugin(插件)一样,去掉不会影响软件的运行。插件可以加到 Blender 的用户偏好设置目录里,或者就在你所编辑的.blend文件里。前者需要你手动开启才能使用;后者勾选 Text Editor 里的 Register 选框后会 Blender 在加载的时候启用,或者通过点击 Register 选框旁边的 Run Script(快捷键Alt + P)运行。

  Blender 插件的路径是 C:\Program Files\Blender Foundation\Blender\2.76\scripts\addons,我假设你用的也是64位的2.76版本Blender软件,而且没有修改安装路径。比如常用的图片作为平面加载的插件对应文件:io_import_images_as_planes.py,启用的话进入用户偏好设置(快捷键Ctrl + Alt + U)的 Add-ons Tab,搜索过滤,勾选后面的选框,在菜单的 File > Import 里多出了一行 Images as Planes。

  插件里定义了一个或多个operator,恕我没有翻译成数学里的算子、C++ 里面的操作符。Blender 的所有 UI 功能是通过 operator 完成的,operator 可绑定到菜单、按钮或快捷键上供调用。operator 的基类是 bpy.types.Operator,自定义 operator 时需要继承自它。

  好了,我们学习写插件吧!
  打开 Blender 的 Text Editor ,我假设你对 Blender 的界面布局有了初步了解。Text Editor 可当 README 使用,程序员在接触新的项目工程时,通过 README 大概了解其功能,同样,Blender 工程在退出时会保存当前界面,其他人打开时,文字简介第一眼就看得到。你可以写入自己的博客地址和大名让旁人围观学习。

  我们的第一个Operator,say hello to Blender!

import bpy

class HelloWorldOperator(bpy.types.Operator):
  bl_idname = "wm.hello_world"
  bl_label = "Hello World"   def execute(self, context):
    print("Hello World!")
    return {'FINISHED'} bpy.utils.register_class(HelloWorldOperator)

  operator 使用前必须注册,bl_idname 顾名思义,就是 operator 的名字,写法像包名,用点号分割,点号左边的名必须属于 bpy.ops 之一,在 Blender 内部的 Python 控制台下,dir(bpy.ops) 列出所有的名字。bl_idname 对内使用,唯一标识符;bl_label 对外可见,UI文本,可以空格搜索;bl_description 是提示信息,鼠标指针停放其上可见,如果bl_label意思明了,也可以不用填写。Python 不区分单引号与双引号,都可以括字符串。或者说,C/C++ 用单引号与双引号区分字符与字符串,但 Python 统一解释成字符串。

>>> type(bpy.ops)
<class 'bpy.ops.BPyOps'>

>>> dir(bpy.ops)
['action', 'anim', 'armature', 'boid', 'brush', 'buttons', 'camera', 'clip', 'cloth', 'console', 'constraint', 'curve', 'cycles', 'dpaint', 'ed', 'export_anim', 'export_mesh', 'export_scene', 'file', 'fluid', 'font', 'gpencil', 'graph', 'group', 'image', 'import_anim', 'import_curve', 'import_image', 'import_mesh', 'import_scene', 'info', 'lamp', 'lattice', 'logic', 'marker', 'mask', 'material', 'mball', 'mesh', 'nla', 'node', 'object', 'outliner', 'paint', 'paintcurve', 'palette', 'particle', 'pose', 'poselib', 'ptcache', 'render', 'rigidbody', 'safe_areas', 'scene', 'screen', 'script', 'sculpt', 'sequencer', 'sketch', 'sound', 'surface', 'text', 'texture', 'time', 'transform', 'ui', 'uv', 'view2d', 'view3d', 'wm', 'world']

>>> print("Hello World!")
Hello World!

>>> bpy.ops.wm.hello_world()
Hello World!
{'FINISHED'}

>>>

  脚本里的一行 print("Hello World!") 一行,输出 Hello World! 信息到控制台,直接运行是看不到输出结果的。你可以像上面一样,在控制台输入 bpy.ops.wm.hello_world() 测试,注意不要有前导空格。

  下面稍微修改一下,来一个 UI 版的 HelloWorld。

import bpy

class HelloWorldOperator(bpy.types.Operator):
  bl_idname = "wm.hello_world"
  bl_label = "Hello World"
  bl_description = "This is an operator test."   def execute(self, context):
    #print("Hello World!")
    self.report({'INFO'}, "Hello World!")
    return {'FINISHED'}   def invoke(self, context, event):
    wm = context.window_manager
    return wm.invoke_props_dialog(self) bpy.utils.register_class(HelloWorldOperator)

  上面命令行版的 HelloWorld,替换一下 self.report({'INFO'}, "Hello World!")
  在3D视图里敲击空格键,输入 Hello World 就可以搜索到,注意是实时搜索,输入几个字母就可以看见条目了。鼠标点击这个条目,会弹出一个对话框,标题就是bl_label,点击OK后消失,Blender 界面菜单一行 logo 附近会 toast 显示 "Hello World!" 消息。你也可以在 Python 控制台输入 bpy.ops.wm.hello_world() 测试新定义的 operator。

>>> bpy.ops.wm.hello_world()
Info: Hello World!
{'FINISHED'}

>>>

  invoke 方法完成后需要返回字符串集合,即用{}括起,表示一个set。告诉 Blender operator 在运行状态 {'RUNNING_MODAL'},还是取消了操作 {'CANCELLED'} 等,return {"FINISHED"} 则表示成功执行。

  第二个operator,添加一个正四面体(regular tetrahedron)。
  汉语与英语比的一个优势是——星期一到日、一月到十二月、正四面体到正二十面体都是用数字表示的,记住数字就会写;而英语撇开了数字,光月份就有十二个单词,英语的词汇量就是这么增加上去的。
  四面体其实就是三棱锥,但正三棱锥不一定是正四面体!正三棱锥要求侧面是等腰三角形,正四面体要求是等边三角形。关于正四面体的几何性质,希望大家还记得。正四面体是三维空间最简单的几何体,甲烷CH4的分子模型可以当成正四面体来讲解,碳原子作为外界面的圆心。二面角的角度为arccos(1/3),取碳原子为坐标原点,则四个氢原子的坐标为Blender 脚本之 Operator 初探Blender 脚本之 Operator 初探,任两个氢原子距离为2。
  正四面体可以从圆锥体构造,添加圆锥 (Shift + A 后选择Cone),左边面板的 vertices 选择3,得到三菱锥后,用球形化(Shift + Alt + S) 或通过菜单 Object > Transform >  To Sphere 就可以了。

  正四面体也很容易从正方体构造出,选取正方体的对面上交叉的对角线,连接这四个顶点,就可以构成正四面体。像这样:
Blender 脚本之 Operator 初探

  Blender采用BU(Blender Unit)单位,正四面体的边长为1,顶点(0, -1 / math.sqrt(3),0),(0.5, 1 / (2 * math.sqrt(3)), 0), (-0.5, 1 / (2 * math.sqrt(3)), 0),(0, 0, math.sqrt(2 / 3))。上面用系统的正方体(边长为2BU)构造的话,边长为2*sqrt(2),顶点(1, 1, 1),(-1, -1, 1), (1, -1, -1),(-1, 1, -1),顶点都是整数哦!

import bpy
from math import sqrt
from mathutils import Vector class MakeTetrahedron(bpy.types.Operator):
bl_idname = "mesh.make_tetrahedron"
bl_label = "Add Tetrahedron" def invoke(self, context, event):
vertices = \
[
Vector((0, -1/sqrt(3),0)),
Vector((0.5, 1/(2 * sqrt(3)), 0)),
Vector((-0.5, 1/(2 * sqrt(3)), 0)),
Vector((0, 0, sqrt(2/3))),
]
edges = []
faces = [[0, 1, 2], [0, 1, 3], [1, 2, 3], [2, 0, 3]] tetrahedron = bpy.data.meshes.new("Tetrahedron")
tetrahedron.from_pydata(vertices, edges, faces)
tetrahedron.update()
object = bpy.data.objects.new("Tetrahedron", tetrahedron)
context.scene.objects.link(object)
return {"FINISHED"}
#end invoke
#end MakeTetrahedron bpy.utils.register_class(MakeTetrahedron)

  内容多一行写不下的话,个人喜欢括号列对齐,需要在行尾添加反斜杠,表示续行,不写会报错的。如果不喜欢这种写法,可以下一行的括号拿上来就成。
  Python 支持 UTF-8 编码,也就是说类名、方法名、变量名可以用 Unicode 字符,当然包括中文。如果你不知道正四面体的英文是tetrahedron,请不要用拼音代替。我见过一些新手写Java代码,不懂的单词懒得查字典,就用拼音(比如阶乘函数写成 jiecheng),看着就想揍人,你写中文,编译器又不会怪你。不过话又说回来,最好用 ASCII 字符,注意下代码工程是用美式英语还是英式英语,其他字符可出现在注释里。

  上面的代码中,先给出了正四面体的数据(如果仅仅是要一个四面体,可以 Mesh > Add Cone,Vertices 选择3就可以了),vertices/edges/faces。Mesh.from_pydata函数利用 V/E/F 这些数据构造出网面。Python 跟 C/C++/Java一样,索引从0开始计数,MATLAB/Lua 从1开始计数。从0开始计数是有很多好处的。上面提供 faces 数据,edges 留空,表示实体(Solid)模型,如果提供以下数据,则会得到正四面体的线框(wireframe)模型。faces的第一个面[0, 1, 2] 表示由线段 [0, 1], [1, 2], [2, 0] 构成的面,最后一个线段用来封闭面。注意面不一定得是三角形。而且索引的顺序需要保持一致,要么都顺时针,要么都逆时针,否则后期需要翻转法线(Ctrl + N)。

    vertices = ... # same as above
edges = [[0, 1], [0, 2], [0, 3], [1, 2], [2, 3], [3, 1]]
faces = []

  注意要么提供 edges 数据,要么提供 faces 数据,两者都提供的话很可能导致 Blender 崩溃。(好吧,我告诉了你一种让Blender崩溃的方法。)你可以用数学公式生成顶点、边、面的数据,赋值给上面的 vertices、edges、faces 变量,在导入这些数据后,Mesh 数据有变,需 update() 一下。
  网面创建了,接下来 mesh 附属到 object datablock,最后链接到场景里。好了,我们完成了。最后一句的注册 register_class 时有必要的,不然按空格是搜索不到的。上面只能通过空格键搜索使用,如果想在UI界面调用话,需要让 bpy.types.Panel 登场了。

  我假设你熟悉 Python 语言,知道类继承的写法,知道缩进的意思。如果你在运行的时候出现语法错误,Blender 会弹出窗口显示出来,可能是混用了tab和空格,也可能是其他小错误,修改正确后重新执行即可。

Blender 脚本之 Operator 初探

  用上我们的operator,下面创建了甲烷CH4分子的棍棒模型。
Blender 脚本之 Operator 初探

参考:
Operators (bpy.ops)Operator (bpy.struct)
Blender interface with Python scripts