zoukankan      html  css  js  c++  java
  • 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

     

  • 相关阅读:
    SQL Server 2008 数据库回滚到某个时间点
    SQL Server 2008以上误操作数据库恢复方法——日志尾部备份
    C# BindingSource
    何谓SQL Server参数嗅探
    mongodb获取具体某一天的查询语句
    给MongoDB添加索引
    MongoDB 学习笔记四 C#调用MongoDB
    Access MongoDB Data with Entity Framework 6
    Ruby 和 OpenSSL CA 证书的问题
    解决方法:配置群集时# gem install redis 报错:Unable to require openssl, install OpenSSL and rebuild ruby
  • 原文地址:https://www.cnblogs.com/liaocheng/p/15603835.html
Copyright © 2011-2022 走看看