注:以下内容基于 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 () { /* 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) == ) {
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, );
if (ot) {
return WM_operator_name_call_ptr(C, ot, context, properties);
} return ;
}

  在 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", ); ...
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

static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(arg))
{
... #ifndef WITH_HEADLESS
extern char datatoc_splash_png[];
extern int datatoc_splash_png_size; extern char datatoc_splash_2x_png[];
extern int datatoc_splash_2x_png_size;
ImBuf *ibuf;
#else
ImBuf *ibuf = NULL;
#endif
... }

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

static int wm_operator_invoke(
bContext *C, wmOperatorType *ot, wmEvent *event,
PointerRNA *properties, ReportList *reports, const bool poll_only)
{
... if (op->type->invoke && event) {
wm_region_mouse_co(C, event); if (op->type->flag & OPTYPE_UNDO)
wm->op_undo_depth++; retval = op->type->invoke(C, op, event);
OPERATOR_RETVAL_CHECK(retval); if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
wm->op_undo_depth--;
}
else if (op->type->exec) {
if (op->type->flag & OPTYPE_UNDO)
wm->op_undo_depth++; retval = op->type->exec(C, op);
OPERATOR_RETVAL_CHECK(retval); if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm)
wm->op_undo_depth--;
}
else {
/* debug, important to leave a while, should never happen */
printf("%s: invalid operator call '%s'\n", __func__, ot->idname);
} ...
}

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 type return flags: exec(), invoke() modal(), return values */
enum {
OPERATOR_RUNNING_MODAL = ( << ),
OPERATOR_CANCELLED = ( << ),
OPERATOR_FINISHED = ( << ),
/* add this flag if the event should pass through */
OPERATOR_PASS_THROUGH = ( << ),
/* in case operator got executed outside WM code... like via fileselect */
OPERATOR_HANDLED = ( << ),
/* used for operators that act indirectly (eg. popup menu)
* note: this isn't great design (using operators to trigger UI) avoid where possible. */
OPERATOR_INTERFACE = ( << ),
}; #define OPERATOR_FLAGS_ALL ( \
OPERATOR_RUNNING_MODAL | \
OPERATOR_CANCELLED | \
OPERATOR_FINISHED | \
OPERATOR_PASS_THROUGH | \
OPERATOR_HANDLED | \
OPERATOR_INTERFACE | \
) /* sanity checks for debug mode only */
#define OPERATOR_RETVAL_CHECK(ret) (void)ret, BLI_assert(ret != 0 && (ret & OPERATOR_FLAGS_ALL) == ret)

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 之 Splash 代码分析的更多相关文章

  1. Android代码分析工具lint学习

    1 lint简介 1.1 概述 lint是随Android SDK自带的一个静态代码分析工具.它用来对Android工程的源文件进行检查,找出在正确性.安全.性能.可使用性.可访问性及国际化等方面可能 ...

  2. pmd静态代码分析

    在正式进入测试之前,进行一定的静态代码分析及code review对代码质量及系统提高是有帮助的,以上为数据证明 Pmd 它是一个基于静态规则集的Java源码分析器,它可以识别出潜在的如下问题:– 可 ...

  3. [Asp.net 5] DependencyInjection项目代码分析-目录

    微软DI文章系列如下所示: [Asp.net 5] DependencyInjection项目代码分析 [Asp.net 5] DependencyInjection项目代码分析2-Autofac [ ...

  4. [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(5)(IEnumerable<>补充)

    Asp.net 5的依赖注入注入系列可以参考链接: [Asp.net 5] DependencyInjection项目代码分析-目录 我们在之前讲微软的实现时,对于OpenIEnumerableSer ...

  5. 完整全面的Java资源库(包括构建、操作、代码分析、编译器、数据库、社区等等)

    构建 这里搜集了用来构建应用程序的工具. Apache Maven:Maven使用声明进行构建并进行依赖管理,偏向于使用约定而不是配置进行构建.Maven优于Apache Ant.后者采用了一种过程化 ...

  6. STM32启动代码分析 IAR 比较好

    stm32启动代码分析 (2012-06-12 09:43:31) 转载▼     最近开始使用ST的stm32w108芯片(也是一款zigbee芯片).开始看他的启动代码看的晕晕呼呼呼的. 还好在c ...

  7. 常用 Java 静态代码分析工具的分析与比较

    常用 Java 静态代码分析工具的分析与比较 简介: 本文首先介绍了静态代码分析的基 本概念及主要技术,随后分别介绍了现有 4 种主流 Java 静态代码分析工具 (Checkstyle,FindBu ...

  8. SonarQube-5.6.3 代码分析平台搭建使用

    python代码分析 官网主页: http://docs.sonarqube.org/display/PLUG/Python+Plugin Windows下安装使用: 快速使用: 1.下载jdk ht ...

  9. angular代码分析之异常日志设计

    angular代码分析之异常日志设计 错误异常是面向对象开发中的记录提示程序执行问题的一种重要机制,在程序执行发生问题的条件下,异常会在中断程序执行,同时会沿着代码的执行路径一步一步的向上抛出异常,最 ...

随机推荐

  1. 微软unity 注入mvc

    首先获取开源unity ,引用, 新建UnityDependencyResolver 继承IDependencyResolver,代码如下: public class UnityDependencyR ...

  2. AVL树

    AVL树 在二叉查找树(BST)中,频繁的插入操作可能会让树的性能发生退化,因此,需要加入一些平衡操作,使树的高度达到理想的O(logn),这就是AVL树出现的背景.注意,AVL树的起名来源于两个发明 ...

  3. easyui如何动态改变列的编辑属性

    动态改变列的编辑属性 var tt=$('#dg').datagrid('getColumnOption', 'yearContent'); //通过列名获得此列 tt.editor={type:'t ...

  4. C# WinForm 技巧:控件截图

    Point screenPoint = 控件.PointToScreen(new Point()); Rectangle rect = new Rectangle(screenPoint, chart ...

  5. Oracle TO_DATE 日期格式

    Oracle trunc()函数的用法 --Oracle trunc()函数的用法/**************日期********************/1.select trunc(sysdat ...

  6. 移动端城市选择JavaScript插件(基于WG的城市选择插件的修改版本)

    周末的时候趁着一次机会,拿WG(博客)开发的城市选择插件改了一个移动端可以直接用的城市选择插件. 原版插件是基于原声JavaScript写的,在此先感谢作者. 我做的只是依照肯德基注册会员的页面的交互 ...

  7. Jquery制作--焦点图淡出淡入

    之前写了一个焦点图左右轮播的,感觉淡出淡入用得也比较多,就干脆一起放上来啦.这个容器用了百分比宽度,图片始终保持居中处理,定宽或者自适应宽度都是可以的. 兼容到IE6+以上浏览器,有淡出淡入速度和切换 ...

  8. Python 动态创建函数【转】

    知乎上也有相似的问题 偶然碰到一个问题,初想是通过动态创建Python函数的方式来解决,于是调研了动态创建Python函数的方法. 定义lambda函数 在Python中定义lambda函数的写法很简 ...

  9. 我的LaTeX中文文档模板

    中文LaTeX处理模板 环境MiTex内核 编辑环境WinEdit 源码如下: \documentclass[a4paper,12pt]{article} \usepackage{CJK} %设定字号 ...

  10. canvas画圆(一)

    仿第一次效果