addon(插件)用来扩展 Blende r的功能,跟其他软件里的 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 对外可见。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" 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,添加一个正四面体(tetrahedron)。
汉语与英语比的一个优势是————星期一到日、一月到十二月、正四面体到正二十面体都是用数字表示的,记住数字就会写;而英语撇开了数字,光月份就有十二个单词,英语的词汇量就是这么增加上去的。
关于正四面体的几何性质,希望大家还记得。
正四面体是最简单的几何体。其外接球新与任意两个顶点构成的角度为pi-arccos(1/3);
正四面体也很容易从正方体构造出,选取正方体的对面上交叉的对角线,连接这四个顶点,就可以构成正四面体。
系统的add Cube/Cylinder/Sphere等,在TODO
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 def register(): bpy.utils.register_class(MakeTetrahedron) def unregister(): bpy.utils.unregister_class(MakeTetrahedron) if __name__ == "__main__": register()
内容多一行写不下的话,个人喜欢括号列对齐,需要在行尾添加反斜杠,表示续行,不写会报错的。如果不喜欢这种写法,可以下一行的括号拿上来就成。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)模型。
vertices = ... # same as above edges = [[0, 1], [0, 2], [0, 3], [1, 2], [2, 3], [3, 1]] faces = []
注意要么提供 edges 数据,要么提供 faces 数据,两者都提供的话很可能导致 Blender 崩溃。(好吧,我告诉了你一种让Blender崩溃的方法。)导入 V/E/F 后,Mesh 数据有变,需 update() 一下。
网面创建了,接下来 mesh 附属到 object datablock,最后链接到场景里。好了,我们完成了。最后一句的注册 register_class 时有必要的,不然按空格是搜索不到的。
我假设你熟悉 Python 语言,知道类继承的写法,知道缩进的意思。如果你在运行的时候出现语法错误,Blender 会弹出窗口显示出来,可能是混用了tab和空格,修改正确后重新执行。