文档章节

Blender 源码学习--Operator

openthings
 openthings
发布于 2015/01/28 13:48
字数 1328
阅读 1737
收藏 5

Blender 的 c 语言 api 与 python api 颇为相似。[感觉像Python的超级扩展]

Mesh Subdivide

下面是对 blender 中的 mesh subdivide operator 代码的分析

Registration

首先我们需要在 window manager 中注册 operator, 编写的注册函数将会在启动时调用.

void MESH_OT_subdivide(wmOperatorType *ot){
    /* identifiers */
    ot->name= "Subdivide";
    ot->description= "Split selected faces into smaller faces.";
    ot->idname= "MESH_OT_subdivide";
 
    /* api callbacks */
    ot->exec= subdivide_exec;
    ot->poll= ED_operator_editmesh;
 
    /* flags */
    ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
 
    /* properties */
    RNA_def_int(ot->srna, "number_cuts", 1, 1, 10, "Number of Cuts", "", 1, INT_MAX);}

第一行

void MESH_OT_subdivide(wmOperatorType *ot)

其中 OT 是指 operator type.

    /* identifiers */
    ot->name= "Subdivide";
    ot->description= "Split selected faces into smaller faces.";
    ot->idname= "MESH_OT_subdivide";
  • ot->name 相当于 bl_label, 用于显示ui

  • ot->description 相当于 bl_description, tooltip

  • ot->idname 相当于 bl_idname

  • MESH_OT_subdivide 相当于 mesh.subdivide, 必须唯一

    /* api callbacks */ot->exec= subdivide_exec;ot->poll= ED_operator_editmesh;ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;

相当于python api 中的 exec 和 poll, ot->flag 相当于 bl_options

    prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, INT_MAX, "Number of Cuts", "", 1, 10);

    PropertyRNA *RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, int default_value,
                     int hardmin, int hardmax, const char *ui_name, const char *ui_description,
                     int softmin, int softmax)

这里可以看一下 RNA_def_int 的定义, identifier, 相当于变量名, 用来访问该变量, 其他都挺直观的, 不过不太清楚 hardmaxsoftmax 有什么区别, 不过一般 hard 更大

WM

void ED_operatortypes_mesh(void){
    ...
    WM_operatortype_append(MESH_OT_subdivide);
    ...}

注册该 operator, 在这里可以看到所有注册的关于mesh的operator

Poll

int ED_operator_editmesh(bContext *C){
    Object *obedit= CTX_data_edit_object(C);
    if(obedit && obedit->type==OB_MESH)
        return NULL != ((Mesh *)obedit->data)->edit_mesh;
    CTX_wm_operator_poll_msg_set(C, "selected object isn't a mesh or not in editmode");
    return 0;}

该函数返回是否可以运行, 首先通过 bContext 获得 object, 然后判断是否是 mesh, 是否有mesh, 以及通过 CTX_wm_operator_poll_msg_set 告知信息

Exec

运行函数, 用于没有用户交互时使用, 和 transform operator 正好相反

static int subdivide_exec(bContext *C, wmOperator *op){
    Scene *scene = CTX_data_scene(C);
    Object *obedit= CTX_data_edit_object(C);
    EditMesh *em= BKE_mesh_get_editmesh((Mesh *)obedit->data);
    int cuts= RNA_int_get(op->ptr,"number_cuts");
 
    esubdivideflag(obedit, em, cuts);
 
    DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
    WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit);
 
    return OPERATOR_FINISHED;}

首先是函数声明

static int subdivide_exec(bContext *C, wmOperator *op)

两个参数, 一个是 context, 和 python 中的 context 类似, 第二个是 wmOperator, 注意这个和注册时的 wmOperatorType 不一样, 返回值用于返回 Operator 是否运行成功

int cuts = RNA_int_get(op->ptr, "number_cuts");

op->ptr 用于获得 RNA, RNA_int_get 用于获取 int properties, number_cuts 为之前声明的变量id

DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);// EDBM_update_generic(em, true, true); in new blender code

在完成操作以后, 我们需要通知对此数据有依赖的操作, DAG_id_tag_update 用于通知 dependency graph, WM_main_add_notifier 用于通知窗口系统

3D View Zoom

Registration

void VIEW3D_OT_zoom(wmOperatorType *ot){
    /* identifiers */
    ot->name = "Zoom View";
    ot->description = "Zoom in/out in the view";
    ot->idname = "VIEW3D_OT_zoom";

    /* api callbacks */
    ot->invoke = viewzoom_invoke;
    ot->exec = viewzoom_exec;
    ot->modal = viewzoom_modal;
    ot->poll = ED_operator_region_view3d_active;
    ot->cancel = viewzoom_cancel;

    /* flags */
    ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER;
    // OPTYPE_BLOCKING 表示获得所有鼠标事件, 包括在窗口外部

    RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX);
    RNA_def_int(ot->srna, "mx", 0, 0, INT_MAX, "Zoom Position X", "", 0, INT_MAX);
    RNA_def_int(ot->srna, "my", 0, 0, INT_MAX, "Zoom Position Y", "", 0, INT_MAX);}

Poll

现在的代码和 2.5 有点出入, 以现在的为准

// context.cRegionView3D *CTX_wm_region_view3d(const bContext *C){
    ScrArea *sa = CTX_wm_area(C);
    // 相当于 context.area
    ARegion *ar = CTX_wm_region(C);
    // 相当于 context.region
    // region 是 area 的 subcontext

    if (sa && sa->spacetype == SPACE_VIEW3D)
    // context.area.space.type == 'VIEW_3D'
        if (ar)
            return ar->regiondata;
            // context.region_data
    return NULL;}int ED_operator_region_view3d_active(bContext *C){
    if (CTX_wm_region_view3d(C))
        return true;

    CTX_wm_operator_poll_msg_set(C, "expected a view3d region");
    return false;}

Invoke

static int viewzoom_invoke(bContext *C, wmOperator *op, wmEvent *event)// 和 exec 不同的是, 多了一个 event, 可以用来获得鼠标事件{
    if(RNA_property_is_set(op->ptr, "delta")) {
        return viewzoom_exec(C, op);
    }
    // 如果已经定义了 delta property, 直接运行
    else {
        /* makes op->customdata */
        viewops_data(C, op, event);
        // 初始化信息, 存入 op->customdata, 这是一个 void* 指针, 可以用于存临时信息
 
        /* add temp handler */
        WM_event_add_modal_handler(C, &CTX_wm_window(C)->   handlers, op);
        // WM_event_add_modal_handler(C, op); in blender 2.72
        // 添加一个 modal hander, 和 python 中的 context.window_manager.modal_handler_add(self) 一样, 这个语句同时会拦截其他 event handler
 
        return OPERATOR_RUNNING_MODAL;
        // 表明现在开始运行modal
    }}

Modal

static int viewzoom_modal(bContext *C, wmOperator *op, wmEvent *event){
    ViewOpsData *vod= op->customdata;
 
    /* execute the events */
    switch(event->type) {
        case MOUSEMOVE:
            viewzoom_apply(vod, event->x, event->y);
            break;
        // 将鼠标移动应用给 viewzoom
 
        default:
            /* origkey may be zero when invoked from a button   */
            if(ELEM3(event->type, ESCKEY, LEFTMOUSE, RIGHTMOUSE) || (event->type==vod->origkey && event->val==0)) {
                request_depth_update(CTX_wm_region_view3d(C));
 
                MEM_freeN(vod);
                op->customdata= NULL;
 
                return OPERATOR_FINISHED;
            }
    }
 
    return OPERATOR_RUNNING_MODAL;}

简易实现 translate

这里仿照这个 Python 脚本实现一个C版本, 作为练习, 因为新的 api 和 wiki 有些出入

/* my transform begin */#include "BKE_object.h"#include "BKE_depsgraph.h"static int mytransform_exec(bContext *C, wmOperator *op){
    float value[2];
    PropertyRNA *prop = RNA_struct_find_property(op->ptr, "value");
    RNA_property_float_get_array(op->ptr, prop, value);
    // 从 property 获得变量

    Object *obj = CTX_data_active_object(C);
    obj->loc[0] = value[0];
    obj->loc[1] = value[1];
    // 设置 object location

    DAG_id_tag_update(&obj->id, OB_RECALC_OB);
    WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, obj);
    // 更新 dependency, 添加 notifier

    return OPERATOR_FINISHED;}static int mytransform_modal(bContext *C, wmOperator *op, const wmEvent *event){
    if (event->type == LEFTMOUSE)
        return OPERATOR_FINISHED;

    if (event->type == RIGHTMOUSE)
        return OPERATOR_CANCELLED;

    if (event->type != MOUSEMOVE)
        printf("Uncatched event type %d\n", (int)(event->type));

    PropertyRNA *prop = RNA_struct_find_property(op->ptr, "value");
    float value[2] = { event->x / 100.0f, event->y / 100.0f };

    RNA_property_float_set_array(op->ptr, prop, value);
    // 获得鼠标事件, 设置 property

    mytransform_exec(C, op);

    return OPERATOR_RUNNING_MODAL;}static int mytransform_poll(bContext *C){
    Object *obj = CTX_data_active_object(C);
    if (obj == NULL) {
        CTX_wm_operator_poll_msg_set(C, "No Active Object in context.");
        return false;
    }
    // 判断 active object 是否存在
    else return true;}static int mytransform_invoke(bContext *C, wmOperator *op, const wmEvent *event){
    PropertyRNA *prop = RNA_struct_find_property(op->ptr, "value");

    if (RNA_property_is_set(op->ptr, prop))
        return mytransform_exec(C, op);

    WM_event_add_modal_handler(C, op);
    // 添加 modal handler

    return OPERATOR_RUNNING_MODAL;}static void TRANSFORM_OT_mytransform(struct wmOperatorType *ot) {
    /* identifiers */
    ot->name = "MyTransform";
    ot->description = "Move object in XY plane by mouse move";
    ot->idname = "TRANSFORM_OT_my_transform";
    ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;

    /* api callbacks */
    ot->invoke = mytransform_invoke;
    ot->exec = mytransform_exec;
    ot->poll = mytransform_poll;
    ot->modal = mytransform_modal;

    RNA_def_float_vector(
        ot->srna, "value", 2, NULL, -FLT_MAX, FLT_MAX,
        "Value", "Vector that add to object", -FLT_MAX, FLT_MAX);}/* my transform end */void transform_operatortypes(void){
    .......
    WM_operatortype_append(TRANSFORM_OT_mytransform);
    // 注册 operator}
  • BKE_context.h, 很多操作 context 的函数

  • RNA_access.h, RNA 数据操作函数

  • RNA_struct_find_property

  • event->type == EVT_MODAL_MAP

  • BKE_object.h


本文转载自:http://cjld.github.io/2014/08/16/blendccode/

共有 人打赏支持
openthings
粉丝 286
博文 1052
码字总数 574209
作品 1
东城
架构师
私信 提问
加载中

评论(2)

openthings
openthings
都下了,编译通过了。还只看了一点点。
zruibin
zruibin
blender源码你整个都下下来看了?
Blender的插件开发-Operator操作器(算子)

毫无疑问,Operator是Blender中最为核心的一个对象,而且里面包罗万象(不仅可以操作几何对象,所有的菜单都可以控制,因为Blender其实就是一个三维图形的超级命令解释器嘛!)。 我们先定义...

openthings
2015/01/28
0
0
Blender的插件开发-对象体系探索

按照python的说法一切皆对象,Blender中亦然。 Blender的根对象是bpy,因此任何模块都需要: import bpy 注意: Blender的脚本print("hello")函数是输出信息到启动的控制台的,直接运行的时候...

openthings
2015/01/27
0
3
三维绘图及渲染软件--Blender

Blender是一套三维绘图及渲染软件。它具有跨平台的特性,支持FreeBSD,IRIX,GNU/Linux,Microsoft Windows,Mac OS X,Solaris,及SkyOS。 有了Blender后,喜欢3D绘图的玩家们不用花大钱,也...

匿名
2008/10/25
23.8K
1
首本 Blender 中文译著《Blender大师》即将发布

长久以来,作为开源三维软件的成功典范,Blender以其强大的功能、成功的开发、迅猛的发展态势,在CG创作及三维内容开发的流程中,是不可或缺的开源方案。 目前,Blender的中文教学资料相对匮...

zilangleon
2014/03/25
2.6K
2
使用Python和Blender来学习3D编程

Blender是一个强大的3D建模工具,他提供了非常强大的Python API,可以用来学习3D编程,还可以用来开发游戏。 要注意的是,Blender有两套Python API,一套是用来调用Blender自身的功能,一套是...

yexiaobo1990
2015/07/23
0
0

没有更多内容

加载失败,请刷新页面

加载更多

从 for of 聊到 Generator

你能学到什么 对 for of 更深入的理解 iterator 到底是何方神圣? 数组也是对象,为什么不能用 for of 来遍历对象呢? 如何实现对象的 for of? Generator 又是何方神圣? Generator 有什么用呢...

Jack088
19分钟前
0
0
怎么判断go-sql-driver 安装成功

.下载安装   执行下面两个命令:     下载:go get github.com/Go-SQL-Driver/MySQL     安装:go install github.com/Go-SQL-Driver/MySQL   怎么判断go-sql-driver 安装成功 ...

dragon_tech
27分钟前
0
0
刚入职阿里,告诉你真实的职场生活,兼谈P6、P7、P8的等级

一:拿下offer的人,基本上都有什么特征? 二:为什么选择阿里? 三:阿里的工作氛围什么样? 四:阿里的薪资情况? 五:阿里的晋升空间有多大? 最近部门招聘,很多工程师,包括我在内都参与...

java知识分子
41分钟前
4
0

中国龙-扬科
44分钟前
1
0
windows 安装nvm

1、nvw-windows的官网:https://github.com/coreybutler/nvm-windows/releases 2、选择nvm-setup.zip安装 3、配置环境变量 4、检查nvm是否安装成功 使用管理员权限打开一个命令行。输入nvm v...

灰白发
57分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部