KAlO2
隨筆 - 24 文章 - 2 評論 - 23 閱讀 - 73554
Blender 之 Splash 代碼分析
注:以下內容基於 Blender 2.7x 版本工程,其它低版本可能有改動。
Blender啟動完成時,會出現一個畫面,英文叫Splash。默認是打開的,可以在設置里關閉。在文件菜單里點擊用戶首選項(快捷鍵Ctrl + Alt + U),在彈出的窗口第一個Tab頁面,也就是界面(Interface)的右下角,有一個選項 Show Splash,默認打了勾,關閉然后最下一行的 Save User Settings。這樣,下次啟動的時候就不會出現Splash。
假設你已經下載好了blender工程代碼,下面通過Splash講解C語言Operator的實現,前一篇是關於Python的Operator。
datatoc
Splash資源所在的文件夾是 blender/release/datafiles/ ,一起的還有各種圖標、字體、SVG矢量圖、畫筆以及matcap效果圖。
source/blender/editors/datafiles/CMakeLists.txt 會調用 data_to_c_simple 函數從圖片格式轉換成C代碼(其實就是長度加二進制數據),blender 需要在源代碼路徑外編譯(out of source build) ,對應生成在 blender-build/release/datafiles/ 。
data_to_c_simple(../../../../release/datafiles/splash.png SRC)
data_to_c_simple 函數(cmake的宏)定義在 build_files/cmake/macros.cmake
data_to_c 需要提供file_from、file_to參數,data_to_c_simple 則僅提供 file_from 參數,file_to 的值為 ${file_from}.c。
調用的命令是 datatoc 其工程在 source/blender/datatoc/ 。
該 CMakeLists.txt 的最后一句指明生成 bf_editor_datafiles.lib
blender_add_lib(bf_editor_datafiles "${SRC}" "${INC}" "${INC_SYS}")
bf_editor_datafiles.vcxproj -> blender-build/lib/Debug/bf_editor_datafiles.lib
blender/source/blenderplayer/CMakeLists.txt
bf_editor_datafiles 被包含在 BLENDER_SORTED_LIBS 里,blender.exe blenderplayer.exe 會依賴這些工程庫。
target_link_libraries(blenderplayer ${BLENDER_SORTED_LIBS})
target_link_libraries(blender ${BLENDER_SORTED_LIBS})
這些圖片資源最終轉換成C數組數據鏈接到.exe文件里。
好處是:如果缺少相關資源(寫錯名字、用了中文字符、或者圖片格式誤寫),編譯期就會報錯;如果僅僅是替換掉一張圖片的話,錯誤只能在運行時被發現,導致出錯。
壞處是:不僅僅是換掉圖片資源就好了的,還需要重新編譯。這讓一些想把splash換成自己作品的藝術家望而卻步。
后話:Google Summer Of Code 2016 列出了可配置的Splash想法(Configuration Splash Screen)。
main函數
Blender.sln 解決方案包含多個工程,cmake工具會額外生成 ALL_BUILD / INSTALL / PACKAGE / RUN_TESTS / ZERO_CHECK 等幾個工程。其中,ALL_BUILD與INSTALL相當於makefile里面的make與install命令。
編譯完成,在運行的時候,需要將啟動工程從 ALL_BUILD 修改成 blender,否則會提示 Unable to start program 'build\x64\Debug\ALL_BUILD' 拒絕訪問。(在blender工程上鼠標右鍵后,選擇 Set as StartUp Project 即可)
一切的一切,要從main函數開始……
main函數在 blender 工程里的 source/creator/creator.c ,初始化子系統(圖像、修改器、畫筆、Python、節點、材質等)、處理命令行參數、進入消息循環WM_main()或者退出循環在后台運行(-b參數可以指定在后台渲染)。
BLI_argsAdd(ba, 1, "-b", "--background", "\n\tRun in background (often used for UI-less rendering)", background_mode, NULL);
所有有UI的應用程序會有消息循環機制。Blender 的是在 WM_main(C);
WM_main 的實現在 source/blender/windowmanager/intern/wm.c ,很簡單的幾行代碼。
void WM_main(bContext *C)
{
while (1) {
/* get events from ghost, handle window events, add to window queues */
wm_window_process_events(C);
/* per window, all events to the window, screen, area and region handlers */
wm_event_do_handlers(C);
/* events have left notes about changes, we handle and cache it */
wm_event_do_notifiers(C);
/* execute cached changes draw */
wm_draw_update(C);
}
}
在 WM_main(C); 一行的上面是關於splash的,
if(!G.file_loaded)
WM_init_splash(C);
Blender 有兩個重要的數據結構 Global G; 和 UserDef U; ,見名知意。
Global 的字段 file_loaded 是 bool 類型的,但是字符串全局搜索到的是賦值為整形1,其實就是true。
source/blender/windowmanager/intern/wm_operators.c: G.file_loaded = 1; /* prevents splash to show */
file_loaded 為 true 則阻止加載splash,否則啟動時加載splash。
Splash
關於 splash 的代碼,就在 WM_init_splash(C); 這一行了。
WM是WindowManager的縮寫,C語言缺少 C++ 的 namespace 概念,這樣附帶模塊命名以防沖突。
來看看代碼中有關splash的地方,Blender一個很常見的概念是Operator。
source/blender/windowmanager/intern/wm_init_exit.c
void WM_init_splash(bContext *C)
{
if ((U.uiflag & USER_SPLASH_DISABLE) == 0) {
wmWindowManager *wm = CTX_wm_manager(C);
wmWindow *prevwin = CTX_wm_window(C);
if (wm->windows.first) {
CTX_wm_window_set(C, wm->windows.first);
WM_operator_name_call(C, "WM_OT_splash", WM_OP_INVOKE_DEFAULT, NULL);
CTX_wm_window_set(C, prevwin);
}
}
}
U.uiflag的比特位差不多用光了,又開了uiflag2。其中,USER_SPLASH_DISABLE 對應上面用戶偏好里的 Show Splash。
bContext 是很重要的數據結構,通過指針傳遞,函數定義在 source/blender/blenkernel/intern/context.c,很多地方都引用了它,應該定義在 .h 文件里的。
給變量取好名字,更方便讀代碼。WM_operator_name_call 見名知意,根據Operator的名字來調用函數,比如上面就是想調用 "WM_OT_splash" 函數。
int WM_operator_name_call(bContext *C, const char *opstring, short context, PointerRNA *properties)
{
wmOperatorType *ot = WM_operatortype_find(opstring, 0);
if (ot) {
return WM_operator_name_call_ptr(C, ot, context, properties);
}
return 0;
}
在 blender/source/blender/windowmanager/intern/wm_operators.c 你可以看到關於 Operator 的各種操作,通過函數 WM_operatortype_append 可以添加新的 Operator,由於有很多 Operator,Blender 用自己的哈希表 GHash *global_ops_hash 管理,WM_OT_splash 是在 WM_init() 初始化時添加的。
/* called on initialize WM_init() */void wm_operatortype_init(void)
{
/* reserve size is set based on blender default setup */
global_ops_hash = BLI_ghash_str_new_ex("wm_operatortype_init gh", 2048);
...
WM_operatortype_append(WM_OT_splash);
...
}
調用的棧是這樣的
int main(int argc, const char **argv)
WM_init(C, argc, (const char **)argv);
wm_operatortype_init();
WM_operatortype_append(WM_OT_splash);
WM_OT_splash,WM是Operator的種類,_OT_ 是 Operator Type,構成Operator ID名稱的一部分,該函數填充 wmOperatorType 里的字段。
static void WM_OT_splash(wmOperatorType *ot)
{
ot->name = "Splash Screen";
ot->idname = "WM_OT_splash";
ot->description = "Open the splash screen with release info";
ot->invoke = wm_splash_invoke;
ot->poll = WM_operator_winactive;
}
前一篇講到過 name、idname、description。struct wmOperatorType 也給了注釋。name 會在UI上顯示出來的。idname 名字應該和函數名字一致,這里可以用 __func__ 宏的。description 是提示信息,鼠標放在上面會顯示出來。
poll回調函數用來測試該Operator是否可以在當前環境(context)執行。我發現有些人在讀代碼的時候,對變量或函數的取名不在意。poll是輪詢的意思,通過名稱就能猜到要完成的功能。有關函數WM_operator_poll
invoke 就是函數調用了。
因為是static函數,所以只需要在本文件內搜索調用處,其他文件是不能調用的。這也算是一種搜索技巧。
static int wm_splash_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event))
{
UI_popup_block_invoke(C, wm_block_create_splash, NULL);
return OPERATOR_FINISHED;
}
C返回 OPERATOR_FINISHED 的狀態,對應 Python 返回 {"FINISHED"}。
接着就是 wm_block_create_splash 函數了,畫出Splash的地方,Splash上面的跳轉鏈接也寫在這個函數里。
// blender/source/blender/windowmanager/intern/wm_operators.c
splash.png
WITH_HEADLESS 名字看不懂,CMakeLists.txt 里解釋說是不帶圖形界面的編譯(渲染農場,服務端模式),默認是關閉的。
這里引用到了 splash.png 的圖,工程datatoc用來將splash.png圖片資源轉換成長度datatoc_splash_png_size 和二進制的dump datatoc_splash_png,上面講過。
有關函數wm_operator_invoke,在 source/blender/windowmanager/intern/wm_event_system.c,調用棧是這樣的:
int main(int argc, const char **argv)
WM_main(C);
WM_init_splash(C);
WM_operator_name_call(C, "WM_OT_splash", WM_OP_INVOKE_DEFAULT, NULL);
WM_operator_name_call_ptr(C, ot, context, properties);
wm_operator_call_internal(C, ot, properties, NULL, context, false);
wm_operator_invoke(C, ot, event, properties, reports, poll_only);
wm_operator_invoke
invoke和exec是函數指針,要二選一填充,否則就是非法的Operator調用。上面的Splash Operator用的是invoke,它們的函數原型是:
int (*exec)(struct bContext *, struct wmOperator *);
int (*invoke)(struct bContext *, struct wmOperator *, const struct wmEvent *);
invoke 比 exec 多出一個 struct wmEvent* 參數,返回類型是int,其實是下面的無名enum類型。
調用exec或invoke后,都會 OPERATOR_RETVAL_CHECK(ret) 測試一下返回值。
invoke比exec多出的一句是:wm_region_mouse_co(C, event);
OPERATOR_RETVAL_CHECK
Blender的用戶偏好設置是可以保存在.blend文件里的,這是關於Splash的。
source/blender/makesrna/intern/rna_userdef.c
prop = RNA_def_property(srna, "show_splash", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "uiflag", USER_SPLASH_DISABLE);
RNA_def_property_ui_text(prop, "Show Splash", "Display splash screen on startup");
source/blender/makesdna/DNA_userdef_types.h
USER_SPLASH_DISABLE = (1 << 27),
參考:
Context + Operator = Action!
blender工程_blender插件開發入門
金門走狗 2020-12-27 03:33:07 825 收藏 5
文章標簽: blender工程
版權
身為游戲開發者,不想只用blender建模,想寫插件提升工作效率?
一、介紹
我希望這篇文章可以讓你快速入門,早知道插件的套路,少走彎路,這篇文章將先直接快速演示一遍如何創建插件工程,從為blender添加一個簡單實用小功能開始,開始帶大家在接下來的時間逐漸熟悉blender插件開發,然后才是回過頭來,介紹必要的常識資料。
(我想大家在blender畫貼圖后,一定會一遍一遍不厭其煩的手動保存貼圖-UV/Image_Editor-Image-Save All Images,因為blender文件保存時不會保存貼圖等數據,一旦什么時候忘記保存貼圖,在心安理得保存完blender文件后關閉blender猛然想起貼圖沒了,可謂欲吐血。這次要添加的簡單又極其實用的功能便是:保存blender文件時自動保存所有已修改圖片)
建議跟隨本文章演示做一遍,本篇文章創建的基礎工程將在未來不斷填充擴展功能。
本文章使用2.79版本,建議插件入門不要使用2.8版本。
二、創建第一個插件工程
1、進入blender的用戶插件文件夾,創建工程文件
上兩圖貼出了blender的兩個插件路徑。
第一個路徑是用戶目錄插件,目錄:userAppDataRoamingBlender FoundationBlenderscriptsaddons,代表非系統原生插件,blender所有安裝的外置插件最終都會被解壓放置到這個文件夾下。安裝插件可以在blender的addon界面直接選擇zip文件安裝,也可以把zip文件中文件夾直接拖入此目錄完成安裝。。
第二個是系統插件路徑,不建議將自己寫的插件放入此地,此地不少系統插件的代碼可在以后做參考用,值得了解。
請在userAppDataRoamingBlender FoundationBlenderscriptsaddons文件夾下新建工程文件夾HelloAddon,並在HelloAddon文件夾下再次新建三個文件夾“command_operators”"model_data"、"view_panels"。
工程文件布局仿造mvc,在前期,我們會經常與command_operators與view panels打交道,組合blender自身的命令與編寫相關界面,至於數據層自然也都是blender內置的各種數據了,基本不需要自定義數據層,暫不關注model_data文件夾。
我使用的文本編輯器為sublime3,僅有方便打開工程文件與python語法高亮功能:
2、新建基礎腳本
a.HelloAddon文件夾下新建入口腳本__init__.py
bl_info = {
"name": "HelloAddon",
"author": "作者",
"version": (1, 0),
"blender": (2, 79, 0),
"location": "View3D > Tool Shelf >HelloAddon Panel",
"description": "插件描述",
"warning": "",
"wiki_url": "",
"category": "3D View",
}
import bpy
import logging
from . import model_data
from . import command_operators
from . import view_panels
def register():
bpy.utils.register_module(__name__)
command_operators.register()
view_panels.register()
def unregister():
bpy.utils.unregister_module(__name__)
command_operators.unregister()
view_panels.unregister()
if __name__ == "__main__":
register()
以上代碼就是套路,不需要理解,主體為bpy.utils.register_module(__name_),作用是注冊import進來的所有模塊。至於command_operators.register() 與 view_panels.register()則代表其他非模塊相關的注冊。這里的代碼以后要新建工程直接ctrl c即可。
view_panels文件夾底新建腳本_init__.py
import bpy
def register():
pass
def unregister():
pass
3、開始添加自動保存圖片功能
command_operators文件夾下新建腳本save_dirty_images.py
import bpy
from bpy.app.handlers import persistent
from bpy.types import Operator
@persistent
def save_dirty_images(dummy):
bpy.ops.image.save_dirty()
print('save image')
保存文件時自動保存圖片的其中一句關鍵代碼就在這里,即使用了系統命令bpy.ops.image.savedirty()。
擴展:可以在blender的系統腳本中找到此命令的類SaveDirty(Operator) 查看實現path:blender/release/scripts/startup/bl_operators/image.py:
這個類的參數Operator,代表此類為blender可調用的操作類,其他函數中可根據bl_idname中的值直接調用執行此類的execute方法,bl_label為blender界面中直接調用執行此方法的命令名稱,可在blender2.79中空格鍵鍵入SaveAllDirtyImages直接執行此類中的execute中的函數功能。
我們的腳本中首先使用了一個空函數save_dirty_images(dummy)調用命令bpy.ops.image.save_dirty(),即執行系統類SaveDirty(operator)的execute方法,這樣包裝起來是為了后續要將此函數傳入blender的保存文件回調方法中做參數,該方法接受一個函數而不是方法,且@persistent與(dummy)是必須的參數。此外我們在save_dirty_images函數底下 print('save image')輸出語句,以便待會能在控制台看到執行成功的消息。
這里雖然創建了可直接執行的命令,但要使得保存blender時自動保存修改過的圖片,還差了一步:找到blender保存文件的回調函數,並將此類附加上去。
commandoperators文件夾下新建_init__.py腳本
import bpy
from . import(
save_dirty_images,
)
def register():
#bpy.app.handlers.save_pre.append為blender保存文件時的回調
bpy.app.handlers.save_pre.append(save_dirty_images.save_dirty_images)
def unregister():
bpy.app.handlers.save_pre.append(save_dirty_images.save_dirty_images)
好了,我們的目的已經達到了,接下來打開blender,開始看效果!
4、打開blender,啟用插件,測試功能
ctrl+alt+U打開配置界面,啟用HelloAddon,並點擊左下按鈕SaveUserSettings保存設置。
Window/Toggle System Console打開控制台,待會可看到命令執行成功我們要輸出的語句。
回到blender主界面,空格鍵后輸入SaveAllDirtyImages,可以看到我們剛才寫的函數:
回車鍵執行,可看到控制台輸出了“save image”
也可以直接ctrl-s保存blender文件,也會在控制台輸出save image,代表確實執行了保存圖片命令,不過我們目前還未創建圖片,沒能看到效果,創建圖片的步驟留給讀者自行嘗試,在image editor創建圖片后切換到paint模式抹兩筆,先手動將圖片保存到自己要的目錄,然后就可以開始測試了,繼續往圖片上抹,然后ctrl-s保存blender文件,將能觀察到圖片也自動保存更新了。
保存blender文件時自動保存所有更改圖片,從此再無忘記保存圖片的煩惱,各位讀者用起來!
5、功能升級
上述雖然實現了保存文件自動保存圖片功能,但唯一的缺點是其只對已有保存路徑的圖片而言有效,若是新建圖片還未保存指定保存路徑,那么此方法會將其直接忽視掉,原因見前面的系統腳本實現。
所以我們現在來改寫系統的方法,當然不是直接更改系統文件,我們將其實現copy出來,加上一個提示功能:若有新建圖片未指定保存路徑,則自動將圖片保存到blender文件的根目錄下。
代碼如下:
import bpy
import os
from bpy.app.handlers import persistent
from bpy.types import Operator
@persistent
def save_dirty_images(dummy):
unique_paths = set()
for image in bpy.data.images:
if image.is_dirty:
if image.packed_file:
if image.library:
self.report({'WARNING'},
"Packed library image: %r from library %r"
" can't be re-packed" %
(image.name, image.library.filepath))
else:
image.pack(as_png=True)
else:
# 若blender與圖片均無路徑,則忽略此圖片自動保存
# 若blender有路徑而圖片無路徑,根目錄上自動保存
if image.filepath == "":
if not bpy.data.filepath == "":
filepath = CreateUniquePath(os.path.split(bpy.data.filepath)[0] + "", image.name, ".png")
image.filepath = filepath
else:
filepath = bpy.path.abspath(image.filepath,
library=image.library)
if "" not in filepath and "/" not in filepath:
print("Invalid path: " + filepath)
elif filepath in unique_paths:
print("Path used by more than one image: %r" %
filepath)
else:
unique_paths.add(filepath)
image.save()
print('save image')
def CreateUniquePath(base_path, file_name, extension):
path = base_path + file_name + extension
while os.path.isfile(path):
file_name += ".001"
path = base_path + file_name + extension
return path
class SaveDirty(Operator):
"""Save all modified textures"""
bl_idname = "image.save_dirty_images"
bl_label = "SaveAllDirtyImages"
def execute(self, context):
save_dirty_images(None)
return {'FINISHED'}
將代碼更改后,現在能保存新建未指定路徑的圖片,路徑圖片名稱將與blender文件內的圖片名稱相同,若根目錄下已有相同名稱圖片,則保存名稱往后疊加.001。
到這里為止,一個完整而實用的小功能就完成了!
6、為下一篇文章做准備
只有一個看不見摸不着的命令怎么行,本着全都要的原則,下面我們來實現一個UI的hello world!下面先來寫一個界面函數。
view_panels文件夾底下新建腳本hello_panel.py,並修改viewpanels/_init__.py,添加語句
from . import(hello_panel)
view_panels文件夾底下新建腳本hello_panel.py
import bpy
from bpy.types import Panel, Menu, UIList, PropertyGroup
from HelloAddon import command_operators
class HelloWorld(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'TOOLS'
bl_category = 'HelloAddon'
bl_idname = 'hello_world'
bl_label = 'HelloWorld'
# bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
layout.label("你好UI")
layout.operator("image.save_dirty_images", text="保存圖片")
重新打開blender(注意blender插件更改后需要重啟blender):
可以看到T面板已經出現HelloAddon面板,包含HelloWorld標題(由bl_label決定)與Label“你好UI”以及按鈕“保存圖片”。
三、blender腳本插件入門心得
跟着上面做完一個插件功能,現在開始介紹如何自行深入學習blender腳本:
途徑1:打開blender的text editor,很多模板文件可供復讀(制):
途徑2:活用python console中的.后代碼提示快捷鍵自動補全查看方法
默認的智能提示快捷鍵未ctrl+space,對中國人而言實在不友好,我改為了ctrl+alt+/,建議讀者也改為此鍵位。
途徑3:關注Outliner的Data-Blocks面板,極其方便,新手時對blender數據塊摸不着頭腦時務必打開此面板,直接http://bpy.data.xxx點出來,如bpy.data.images['myimage']....、bpy.data.objects['myobj'].data...,此面板下要找什么數據一路沿着+號點下去即可看到,可以說是可視化的代碼自動補全!
途徑4:查詢api(實用性不高)
途徑5:blender python 全球最大愛好者論壇:Stack Exchange
途徑6:查看各類開源插件
blender插件那么多,隨便拿起一個找到想學的功能就可以開始copy了,不需要自己從零走,何樂不為
本篇文章到此結束,有什么想法歡迎提出,下一篇文章內容將根據評論區而定,當然這第一篇文章很可能根本沒什么人看到,那我就按照自己想法繼續走啦。
相關資源:blender:自行編寫或修改的Blender插件-源碼_blender插件開發...
————————————————
版權聲明:本文為CSDN博主「金門走狗」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/weixin_32768455/article/details/112126216
Blender插件初始化范例
目標
- [x] 總結Blender插件初始化范例
總結
插件模板
Blender內部插件實現方式模板功能總結如下:
- 定義了子模塊重加載方式
- 定義了批量加載子模塊的方式
- 插件注冊函數
- 插件注銷函數
模塊總體結構如下:
# 支持子模塊重加載(support reloading sub-modules)if "bpy" in locals():
from importlib import reload
_modules_loaded[:] = [reload(val) for val in _modules_loaded]
del reload
# 定義要加載的模塊
_modules = [
"add_mesh_torus",
...
]
import bpy
# 模塊加載, __import__()相當於 from __name__ import _modules__import__(name=__name__, fromlist=_modules)
_namespace = globals()
_modules_loaded = [_namespace[name] for name in _modules]del _namespace
def register():
from bpy.utils import register_class
for mod in _modules_loaded:
for cls in mod.classes:
register_class(cls)
def unregister():
from bpy.utils import unregister_class
for mod in reversed(_modules_loaded):
for cls in reversed(mod.classes):
if cls.is_registered:
unregister_class(cls)
范例
Blender Foundation\Blender\2.79\scripts\startup\bl_operators\__init__.py
# ##### BEGIN GPL LICENSE BLOCK ####### This program is free software; you can redistribute it and/or# modify it under the terms of the GNU General Public License# as published by the Free Software Foundation; either version 2# of the License, or (at your option) any later version.## This program is distributed in the hope that it will be useful,# but WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the# GNU General Public License for more details.## You should have received a copy of the GNU General Public License# along with this program; if not, write to the Free Software Foundation,# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.## ##### END GPL LICENSE BLOCK #####
# <pep8 compliant>
# support reloading sub-modulesif "bpy" in locals():
from importlib import reload
_modules_loaded[:] = [reload(val) for val in _modules_loaded]
del reload
_modules = [
"add_mesh_torus",
"anim",
"clip",
"console",
"file",
"image",
"mask",
"mesh",
"node",
"object_align",
"object",
"object_randomize_transform",
"object_quick_effects",
"presets",
"rigidbody",
"screen_play_rendered_anim",
"sequencer",
"uvcalc_follow_active",
"uvcalc_lightmap",
"uvcalc_smart_project",
"vertexpaint_dirt",
"view3d",
"wm",
]
import bpy
if bpy.app.build_options.freestyle:
_modules.append("freestyle")
__import__(name=__name__, fromlist=_modules)
_namespace = globals()
_modules_loaded = [_namespace[name] for name in _modules]del _namespace
def register():
from bpy.utils import register_class
for mod in _modules_loaded:
for cls in mod.classes:
register_class(cls)
def unregister():
from bpy.utils import unregister_class
for mod in reversed(_modules_loaded):
for cls in reversed(mod.classes):
if cls.is_registered:
unregister_class(cls)
Blender插件編寫指南
前言
Blender插件是Blender的利器, 用戶可以使用各種插件擴充Blender的功能.
Blender Python插件以bpy.props, bpy.types.Operator, bpy.types.Panel, bpy.types.UILayout, (...)為基礎, 通過用戶自定義包來實現.
插件要點
- 定義操作器
- 定義操作器控制面板(或菜單)
- 注冊/注銷操作器和面板
簡單實例
bl_info = {
"name": "Move X Axis",
"category": "Object",
}
import bpy
class ObjectMoveX(bpy.types.Operator):
"""My Object Moving Script""" # blender will use this as a tooltip for menu items and buttons.
bl_idname = "object.move_x" # unique identifier for buttons and menu items to reference.
bl_label = "Move X by One" # display name in the interface.
bl_options = {'REGISTER', 'UNDO'} # enable undo for the operator.
def execute(self, context): # execute() is called by blender when running the operator.
# The original script
scene = context.scene
for obj in scene.objects:
obj.location.x += 1.0
return {'FINISHED'} # this lets blender know the operator finished successfully.
def register():
bpy.utils.register_class(ObjectMoveX)
def unregister():
bpy.utils.unregister_class(ObjectMoveX)
# This allows you to run the script directly from blenders text editor# to test the addon without having to install it.if __name__ == "__main__":
register()
參考
- Blender插件之操作器(Operator)實戰 1
- Blender之UILayout 2
- Blender插件之Panel 3
- Blender之Property 4
- Addon Tutorial 5