Blender 之 入门

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的实现,前一篇是关于PythonOperator

 

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_fromfile_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_BUILDINSTALL相当于makefile里面的makeinstall命令。
  编译完成,在运行的时候,需要将启动工程从 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); 这一行了。
  WMWindowManager的缩写,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,由于有很多 OperatorBlender 用自己的哈希表 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_splashWMOperator的种类,_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;

}

 

 

  前一篇讲到过 nameidnamedescriptionstruct 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

 

  invokeexec是函数指针,要二选一填充,否则就是非法的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类型。
  调用execinvoke后,都会 OPERATOR_RETVAL_CHECK(ret) 测试一下返回值。
  invokeexec多出的一句是: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所有安装的外置插件最终都会被解压放置到这个文件夹下。安装插件可以在blenderaddon界面直接选择zip文件安装,也可以把zip文件中文件夹直接拖入此目录完成安装。。

 

第二个是系统插件路径,不建议将自己写的插件放入此地,此地不少系统插件的代码可在以后做参考用,值得了解。

 

请在userAppDataRoamingBlender FoundationBlenderscriptsaddons文件夹下新建工程文件夹HelloAddon,并在HelloAddon文件夹下再次新建三个文件夹“command_operators"model_data""view_panels"

 

工程文件布局仿造mvc,在前期,我们会经常与command_operatorsview 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_labelblender界面中直接调用执行此方法的命令名称,可在blender2.79中空格键键入SaveAllDirtyImages直接执行此类中的execute中的函数功能。

我们的脚本中首先使用了一个空函数save_dirty_imagesdummy)调用命令bpy.ops.image.save_dirty(),即执行系统类SaveDirtyoperator)的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.appendblender保存文件时的回调

    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、为下一篇文章做准备

只有一个看不见摸不着的命令怎么行,本着全都要的原则,下面我们来实现一个UIhello 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:打开blendertext editor,很多模板文件可供复读(制):

 

 

途径2:活用python console中的.后代码提示快捷键自动补全查看方法

 

 

默认的智能提示快捷键未ctrl+space,对中国人而言实在不友好,我改为了ctrl+alt+/,建议读者也改为此键位。

途径3:关注OutlinerData-Blocks面板,极其方便,新手时对blender数据块摸不着头脑时务必打开此面板,直接http://bpy.data.xxx点出来,如bpy.data.images['myimage']....bpy.data.objects['myobj'].data...,此面板下要找什么数据一路沿着+号点下去即可看到,可以说是可视化的代码自动补全!

 

 

途径4:查询api(实用性不高)

 

 

途径5blender 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内部插件实现方式模板功能总结如下:

  1. 定义了子模块重加载方式
  2. 定义了批量加载子模块的方式
  3. 插件注册函数
  4. 插件注销函数

模块总体结构如下:

# 支持子模块重加载(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, (...)为基础, 通过用户自定义包来实现.

插件要点

  1. 定义操作器
  2. 定义操作器控制面板(或菜单)
  3. 注册/注销操作器和面板

简单实例

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()

参考

  1. Blender插件之操作器(Operator)实战 1
  2. BlenderUILayout 2
  3. Blender插件之Panel 3
  4. BlenderProperty 4
  5. Addon Tutorial 5

 

 

posted @ 2021-11-25 17:41  廖先生  阅读(848)  评论(0编辑  收藏  举报