【Blender】插件开发笔记
开发环境配置
- 打开设置“界面-开发选项”:
这样可以通过对功能按钮的右键菜单直接复制或查看其源码,以及快速跳转到 API 手册。
- 打开设置“界面-Python 工具提示”:
这样可以通过界面的悬停菜单直接查看该条目在代码中的表示方式。
- 安装 python 环境,下载 fake-bpy-module 包:
这样可以在外部编辑器编写代码,并且有代码代码提示。
- 下载 VSCode 及其插件 Blender-Development:
这样可以快速创建插件项目并能自动导入和调试插件。
插件基本原理
插件是什么?
插件是将代码集成到 Blender 的一种方式,只要符合以下要求,便可被 Blender 识别为插件。
- 插件是一个 Python 包,即一个带有__init__.py 的文件夹。
- 该包带有 register()和 unregister()两个函数。
插件如何被集成?
Blender 识别到插件会主动调用其中的 register 和 unregister 函数,这分别发生在启用插件和禁用插件两个时段,除此之外 Blender 不会调用任何函数。
所以我们必须借助 register 和 unregister 两个时段,主动将我们的“功能”注册给 Blender 编辑器或从中取消注册。
向 Blender 编辑器注册或取消注册是借助 Blender 的内置函数实现的,使用该函数时必须要提供一个参数,而该参数正是用来传递我们的“功能”的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
import bpy
functions = [function1,function2,function3,...]
def register(): for f in functions: bpy.utils.register_class(f)
def unregister(): for f in functions: bpy.utils.unregister_class(f)
|
集成的功能是什么?
注册功能的函数是 Blender 提供的,所以我们的功能也必须符合 Blender 注册函数对参数的要求。
具体来说这些功能实际上是几个 Blender 接口类的实现,目前 Blender 提供以下可用于集成的接口类:
1 2 3 4 5 6
| bpy.types.Panel bpy.types.Menu bpy.types.Operator bpy.types.PropertyGroup bpy.types.KeyingSet bpy.types.RenderEngine
|
具体关于这些接口类的解释,留到后续使用时再进行讲述。
除此之外其实还有些更高级的功能也可以注册,例如网格修改器, 对象类型或着色器节点等,但这些必须要用 C/C++实现,故不做讨论。
Blender 关键概念
内置资源
所有资源都由集合存储管理,资源的创建销毁是通过对集合的增删来实现。
在此我们把 Blender 自带的数据类型,如物体,网格,材质等叫做内置资源。
Blender 中所有的内置资源都有专门的类对应,而它们的实例则全部统一存在几个集合中。
Blender 完全用这些集合控制资源的生命周期,所以资源的创建销毁不能用常规的 new,delete 方式,而是要直接操作这些集合,集合的增删就代表着类的创建销毁。
这些集合可以通过 bpy.data 进行访问,也可以通过“大纲视图-Blender 文件”界面进行可视化访问。
1 2 3 4 5
| import bpy newMaterial = bpy.data.materials.new('New Material') oldMaterial = bpy.data.materials['Old Material'] bpy.data.materials.remove(oldMaterial)
|
自定义属性
若要存储或显示自己的数据,必须借助“自定义属性”功能来实现。
自定义属性是 Blender 提供的一种在 Blender 文件中存储数据的方法,除此之外没有其他方式。
用户可以利用 Blender 提供的属性类创建自己的数据容器,并将其附加到任何一个 Blender 的内置资源上。
不单是存储数据,如果要在面板上显示数据,也必是自定义属性的数据才行。
在代码中利用 bpy.props 空间提供的类就能创建自定义属性对象,或在资源面板的“自定义属性”子面板中也能可视化编辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import bpy
def register(): bpy.types.Scene.my_data = bpy.props.StringProperty()
def unregister(): del bpy.types.Scene.my_data
def my_function(): bpy.data.scenes['Scene'].my_data = 'Hello Blender' print(bpy.data.scenes['Scene'].my_data)
|
运算符
若要实现显示或执行自定义功能,必须借助“运算符”功能来实现。
Blender 中所有的可执行功能也即界面上的按钮都一定对应一个运算符,可以借此在代码中像在用户界面上一样调用这些功能,blender 内置的运算符都存放在 bpy.ops 空间。
同理若要实现自己的功能并显示在界面上也一定要实现自己的运算符。
1 2 3 4 5 6 7 8 9 10 11
|
class MyOperator(bpy.types.Operator): bl_idname = "my.operator"
class MyPanel(bpy.types.Panel): layout.operator(MyOperator.bl_idname, text="MyOperator")
bpy.utils.register_class(MyOperator) bpy.utils.register_class(MyPanel)
|
Blender 框架结构
根据框架结构可以了解到 Blender 中有哪些可操作对象,以及它们的关系,配合英文名搜索,可以快速定位目标对象在代码中的修改位置。
库结构
1 2 3 4 5 6 7 8 9
| bpy.types bpy.props bpy.ops bpy.data bpy.context bpy.utils bpy.msgbus bpy.path bpy.app
|
文件结构
参考“大纲视图-Blender 文件”界面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| bpy.data. window_managers. workspaces. screens. brushes. palettes. worlds. collections. objects. scenes. cameras. lights. meshes. images. materials. linestyles.
|
资源结构
参考“大纲视图-Blender 文件”界面
1 2 3 4 5 6 7 8 9 10 11 12
| bpy.context. scene. view_layers. world. collection. objects. active_object. data. data(camera). data(light). data(mesh). materials.
|
界面结构
参考 https://docs.blender.org/manual/en/latest/interface/index.html#window-system
1 2 3 4 5
| bpy.context.screen. areas. regions. spaces. space(SpaceView3D).
|
SpaceView3D
1 2 3 4 5
| SpaceView3D. region_3d. view_perspective view_camera_offset view_camera_zoom
|
工具结构
图像绘制工具
1 2 3 4 5 6 7 8 9 10 11 12
| bpy.context.tool_settings image_paint. brush. texture_slot stencil_dimension stencil_pos
bpy.context.active_object. active_material. texture_paint_images paint_active_slot
|
常用接口类
Operator
添加自定义操作时必须要实现 Operator。
1 2 3 4 5 6 7 8 9 10 11 12
| class bpy.types.Operator: bl_idname : str bl_label : str
@classmethod def poll(cls, context) -> bool
def execute(self, context) -> Set[str]
|
Panel
添加自定义面板界面时必须要实现 Panel。
1 2 3 4 5 6 7 8 9 10 11 12 13
| class bpy.types.Panel: bl_space_type : str bl_region_type : str bl_label : str
bl_category : str
def draw(self,context) -> None
|
PropertyGroup
自定义参数过多时可以借助 PropertyGroup 实现合并注册。
1 2 3
| class bpy.types.PropertyGroup: ......
|
常用操作
绘制界面
bpy.types.Panel 等类提供名为 layout 的成员变量,该变量提供绘制函数使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class MyPanel(bpy.types.Panel): def draw(self, context): layout = self.layout
layout.label(text="渲染参数")
column = layout.column(align=True) column.prop(context.scene.render, "resolution_x",text="宽度") column.prop(context.scene.render, "resolution_y",text="高度")
layout.operator("view3d.view_center_camera", text="重置摄像机视图")
|
弹出信息
bpy.types.Operator 等类提供名为 report 的成员函数,可以在底部信息栏弹出消息。
1 2 3 4 5 6 7
| class MyOperator(bpy.types.Operator): def execute(self, context): if(context.active_object == None): self.report({"ERROR"}, "当前没有激活的物体") return {"CANCELLED"} return {"FINISHED"}
|
细节提示
- 在注册期间无法访问 bpy.data 和 bpy.context,所以要注册自定义函数必须借助 bpy.types 以类为目标。
- 可以借助“编辑”菜单中的“菜单查找”和“操作搜索”找到注册进 blender 的自定义菜单或运算符。
- 如果要利用插件传递资源,可以在插件中携带 blender 文件,并利用“关联”和“追加”功能实现。
- 如果在自定义运算符的 bl_idname 中使用了非官方的类别,那右键菜单中将缺少指定快捷键功能。
参考资料